比特幣源碼研讀(2)數(shù)據(jù)結(jié)構(gòu)-區(qū)塊Block

上一篇基本認識了bitcoin源碼結(jié)構(gòu)和個模塊代碼的功能满俗,今天看區(qū)塊。

區(qū)塊是組成區(qū)塊的基本單位,我們可以通過bitcoin-cli命令查看一個區(qū)塊的基本信息降盹。

bitcli-cli獲取區(qū)塊信息

接下來我們就在源代碼找一下區(qū)塊的定義谤辜,由于我們并不知道區(qū)塊定義在哪丑念。我們試著全局搜一下block.h(或block.cpp):

搜索block.h

進去發(fā)現(xiàn)還真被我找到了,其實我們在上一篇的bitcoin源碼結(jié)構(gòu)的目錄結(jié)構(gòu)里已經(jīng)說過./private目錄下是區(qū)塊類和交易類的實現(xiàn)渔彰。接下來推正,就讓我們一窺block的究竟植榕。

源碼初窺

CBlockHeader

/** Nodes collect new transactions into a block, hash them into a hash tree,
 * and scan through nonce values to make the block's hash satisfy proof-of-work
 * requirements.  When they solve the proof-of-work, they broadcast the block
 * to everyone and the block is added to the block chain.  The first transaction
 * in the block is a special one that creates a new coin owned by the creator
 * of the block.
 *
 **網(wǎng)絡(luò)中的節(jié)點不斷收集新的交易打包到區(qū)塊中,所有的交易會通過兩兩哈希的方式形成一個Merkle樹
 * 打包的過程就是要完成工作量證明的要求炒瘸,當節(jié)點解出了當前的隨機數(shù)時寝衫,
 * 它就把當前的區(qū)塊廣播到其他所有節(jié)點慰毅,并且加到區(qū)塊鏈上。
 * 區(qū)塊中的第一筆交易稱之為CoinBase交易技俐,是產(chǎn)生的新幣统台,獎勵給區(qū)塊的產(chǎn)生者  
 * 
 * add by chaors 20180419
 */

class CBlockHeader
{
public:
    // header
    int32_t nVersion;       //版本
    uint256 hashPrevBlock;  //上一個區(qū)塊的hash
    uint256 hashMerkleRoot; //包含交易信息的Merkle樹根
    uint32_t nTime;         //時間戳
    uint32_t nBits;         //工作量證明(POW)的難度
    uint32_t nNonce;        //要找的符合POW的隨機數(shù)

    CBlockHeader()          //構(gòu)造函數(shù)初始化成員變量
    {
        SetNull();          
    }

    ADD_SERIALIZE_METHODS;  //通過封裝的模板實現(xiàn)類的序列化

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(this->nVersion);
        READWRITE(hashPrevBlock);
        READWRITE(hashMerkleRoot);
        READWRITE(nTime);
        READWRITE(nBits);
        READWRITE(nNonce);
    }

    void SetNull()          //初始化成員變量
    {
        nVersion = 0;
        hashPrevBlock.SetNull();
        hashMerkleRoot.SetNull();
        nTime = 0;
        nBits = 0;
        nNonce = 0;
    }

    bool IsNull() const
    {
        return (nBits == 0);     //難度為0說明區(qū)塊還未創(chuàng)建贱勃,區(qū)塊頭為空
    }

    uint256 GetHash() const;     //獲取哈希

    int64_t GetBlockTime() const //獲取區(qū)塊時間
    {
        return (int64_t)nTime;
    }
};

CBlock

class CBlock : public CBlockHeader         //繼承自CBlockHeader谤逼,擁有其所有成員變量
{
public:
    // network and disk
    std::vector<CTransactionRef> vtx;      //所有交易的容器

    // memory only
    mutable bool fChecked;                 //交易是否驗證

    CBlock()
    {
        SetNull();
    }

    CBlock(const CBlockHeader &header)
    {
        SetNull();
        *((CBlockHeader*)this) = header;
    }

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(*(CBlockHeader*)this);
        READWRITE(vtx);
    }

    void SetNull()
    {
        CBlockHeader::SetNull();
        vtx.clear();
        fChecked = false;
    }

    CBlockHeader GetBlockHeader() const
    {
        CBlockHeader block;
        block.nVersion       = nVersion;
        block.hashPrevBlock  = hashPrevBlock;
        block.hashMerkleRoot = hashMerkleRoot;
        block.nTime          = nTime;
        block.nBits          = nBits;
        block.nNonce         = nNonce;
        return block;
    }

    std::string ToString() const;
};

CBlockLocator


/** Describes a place in the block chain to another node such that if the
 * other node doesn't have the same branch, it can find a recent common trunk.
 * The further back it is, the further before the fork it may be.
 *
 **描述區(qū)塊鏈中在其他節(jié)點的一個位置,
 *如果其他節(jié)點沒有相同的分支纹坐,它可以找到一個最近的中繼(最近的相同塊)耘子。
 *更進一步地講,它可能是分叉前的一個位置
 */
struct CBlockLocator
{
    std::vector<uint256> vHave;

    CBlockLocator() {}

    explicit CBlockLocator(const std::vector<uint256>& vHaveIn) : vHave(vHaveIn) {}

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        int nVersion = s.GetVersion();
        if (!(s.GetType() & SER_GETHASH))
            READWRITE(nVersion);
        READWRITE(vHave);
    }

    void SetNull()
    {
        vHave.clear();
    }

    bool IsNull() const
    {
        return vHave.empty();
    }
};

. cpp函數(shù)

uint256 CBlockHeader::GetHash() const
{
    return SerializeHash(*this);        //生成256位的哈希值
}

std::string CBlock::ToString() const    //區(qū)塊對象格式化字符串輸出
{
    std::stringstream s;
    s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n",
        GetHash().ToString(),
        nVersion,
        hashPrevBlock.ToString(),
        hashMerkleRoot.ToString(),
        nTime, nBits, nNonce,
        vtx.size());
    for (const auto& tx : vtx) {
        s << "  " << tx->ToString() << "\n";
    }
    return s.str();
}

區(qū)塊結(jié)構(gòu)分析

區(qū)塊是通過前后鏈接構(gòu)成區(qū)塊鏈的一種容器數(shù)據(jù)結(jié)構(gòu)绒障。它由描述區(qū)塊主要信息的區(qū)塊頭和包含若干交易數(shù)據(jù)的區(qū)塊共同組成户辱。區(qū)塊頭是80字節(jié)糙臼,而平均每個交易至少是250字節(jié),而且平均每個區(qū)塊至少包含超過500個交易焚鹊。所以韧献,一個區(qū)塊是比區(qū)塊頭大好多的數(shù)據(jù)體。這也是比特幣驗證交易是否存在采用Merkcle樹的原因璧针。

整體結(jié)構(gòu)

數(shù)據(jù)項 大小(Byte) 描述
Block Size 4 區(qū)塊大小
Block Header 80 區(qū)塊頭信息大小
Transactions m*n(n>=250) 所有交易的列表
Transactions Counter 1-9 交易數(shù)額

比特幣的區(qū)塊大小目前被嚴格限制在1MB以內(nèi)探橱。4字節(jié)的區(qū)塊大小字段不包含在此內(nèi)!

區(qū)塊頭

數(shù)據(jù)項 大小(Byte) 存儲方式 描述
Version 4 小端 區(qū)塊版本绘证,規(guī)定了區(qū)塊遵守的驗證規(guī)則
Previous Block Hash 32 內(nèi)部字節(jié)順序 上一個區(qū)塊哈希值(SHA256 (SHA256(Block Header)))
Merkle Root 32 內(nèi)部字節(jié)順序 Merkle樹根嚷那,包含了所有交易的哈希
Timestamp 4 小端 區(qū)塊產(chǎn)生時間戳,大于前11個區(qū)塊時間戳的平均值腐泻,全節(jié)點會拒絕時間戳超出自己2小時的區(qū)塊
nBitS 4 小端 工作量證明(POW)的目標難度值,當前區(qū)塊難度值需要經(jīng)過Target nBits編碼才能轉(zhuǎn)化為目標哈希值
Nonce 4 小端 用于POW的一個隨機數(shù)构诚,隨著算力增大可能會導致Nonce位數(shù)不夠 協(xié)議規(guī)定時間戳和CoinbaseTransaction信息可修改用于擴展Nonce位數(shù)

區(qū)塊標識符

  • BlockHash 區(qū)塊哈希值铆惑,是通過SHA256算法對區(qū)塊頭信息進行哈希得到的,這個值必須滿足POW的DifficultyTarget鸭津,該區(qū)塊才被認為有效逆趋。同時晒奕,也是區(qū)塊的唯一標識符,可以通過通過bitcoin-cli根據(jù)BlockHash查詢區(qū)塊信息(文章開頭我們就使用過)

  • BlockHeight 區(qū)塊高度魄眉,是用來標示區(qū)塊在區(qū)塊鏈中的位置闷袒。創(chuàng)世區(qū)塊高度為0,每一個加在后面的區(qū)塊晃择,區(qū)塊高度遞增1也物』牵可以通過bitcoin-cli根據(jù)高度查詢區(qū)塊哈希值(文章開頭我們就使用過)

注:BlockHeight并不是唯一標識符,當區(qū)塊鏈出現(xiàn)臨時分叉時坤次,會有兩個區(qū)塊的高度值相同的情況斥赋。

創(chuàng)世區(qū)塊

區(qū)塊鏈上第一個區(qū)塊被稱為創(chuàng)世區(qū)塊,它是所有區(qū)塊的共同祖先洛波。我們可以查看下比特幣的創(chuàng)世區(qū)塊:

創(chuàng)世區(qū)塊

比特幣創(chuàng)始人聰哥在創(chuàng)世區(qū)塊包含了一個隱藏的信息蹬挤。在其Coinbase交易的輸入中包含這樣一句話“The Times 03/Jan/2009 Chancellor on brink of second bailout forbanks.”這句話是泰晤士報當天的頭版文章標題,聰哥這樣做的目的不得而知倦零。但是吨悍,這樣一條非交易信息可以輕而易舉地插入比特幣,這個現(xiàn)象值得深思葫隙。如此躏仇,就不難理解前不久曝光的"不法分子利用比特幣存儲兒童色情內(nèi)容"新聞,當然這種存儲可能遠比聰哥的那句話要更復雜一點糟描。

思考

    1. 我們查看的區(qū)塊信息中书妻,以下不在源碼結(jié)構(gòu)中的字段有什么含義躲履?為什么他們不在源碼定義的區(qū)塊結(jié)構(gòu)中?
      confirmations
      strippedsize
      weight
      mediantime
      bits
      chainwork
  • 2.為什么區(qū)塊的哈希沒有定義在區(qū)塊頭內(nèi)部缤剧?

注:以上問題可能我也沒答案域慷,可以留言我們一起交流共同進步。

對區(qū)塊的認識就告一段落抵窒,下一篇準備去探索比特幣數(shù)據(jù)結(jié)構(gòu)-交易的結(jié)構(gòu)叠骑。

參考文獻

.
.
.
.

互聯(lián)網(wǎng)顛覆世界,區(qū)塊鏈顛覆互聯(lián)網(wǎng)!

--------------------------------------------------20180419 23:20
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茧跋,一起剝皮案震驚了整個濱河市卓囚,隨后出現(xiàn)的幾起案子哪亿,更是在濱河造成了極大的恐慌,老刑警劉巖蝇棉,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篡殷,死亡現(xiàn)場離奇詭異,居然都是意外死亡搀绣,警方通過查閱死者的電腦和手機戳气,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進店門瓶您,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纲仍,“玉大人郑叠,你說我怎么就攤上這事∠绺铮” “怎么了沸版?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵视粮,是天一觀的道長。 經(jīng)常有香客問我笑撞,道長,這世上最難降的妖魔是什么茴肥? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任炉爆,我火速辦了婚禮,結(jié)果婚禮上赴捞,老公的妹妹穿的比我還像新娘郁稍。我一直安慰自己耀怜,他們只是感情好而克,可當我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布左痢。 她就那樣靜靜地躺著,像睡著了一般俊性。 火紅的嫁衣襯著肌膚如雪定页。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天杭煎,我揣著相機與錄音宫峦,去河邊找鬼导绷。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的钦购。 我是一名探鬼主播褂萧,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼导犹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了磕昼?” 一聲冷哼從身側(cè)響起节猿,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤滨嘱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吟榴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躺彬,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡宪拥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年她君,在試婚紗的時候發(fā)現(xiàn)自己被綠了葫哗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡校镐,死狀恐怖鸟廓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牍陌,我是刑警寧澤员咽,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布贝室,位于F島的核電站,受9級特大地震影響滑频,放射性物質(zhì)發(fā)生泄漏误趴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一枣申、第九天 我趴在偏房一處隱蔽的房頂上張望看杭。 院中可真熱鬧楼雹,春花似錦、人聲如沸榨咐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽数焊。三九已至崎场,卻和暖如春谭跨,著一層夾襖步出監(jiān)牢的瞬間答恶,已是汗流浹背萍诱。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工裕坊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人周瞎。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓饵蒂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親彼乌。 傳聞我的和親對象是個殘疾皇子渊迁,可洞房花燭夜當晚...
    茶點故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容