比特币源代码--12--数据结构-区块

区块链是由包含交易信息的区块从后向前有序链接起来的数据结构,区块是组成区块链的基本组成单位,也是交易信息的载体。

区块的定义声明文件在src/private下面。

/** 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.
 *
 **网络中的节点不断收集新的交易打包到区块中,所有的交易会通过两两哈希的方式形成一个Merkle树
 * 打包的过程就是要完成工作量证明的要求,当节点解出了当前的随机数时,
 * 它就把当前的区块广播到其他所有节点,并且加到区块链上。
 * 区块中的第一笔交易称之为CoinBase交易,是产生的新币,奖励给区块的产生者  
 * 
 */

高度

区块高度是指该区块在区块链的中的位置。

比特币源代码--12--数据结构-区块

区块结构

区块是一种被包含在公开账簿(区块链)里的聚合了交易信息的容器数据结构。它由一个包含元数据的区块头和紧跟其后的构成区块主体的一长串交易组成。区块头是80字节,而平均每个交易至少是250字节,而且平均每个区块至少包含超过500个交易。

比特币源代码--12--数据结构-区块

字节长度 字段 说明
4 区块大小 用字节表示的该字段之后的区块大小
80 区块头 组成区块头的几个字段
1-9 交易计数器 该区块包含的交易数量,包含coinbase交易
不定 交易列表 记录在区块里的交易信息

比特币源代码--12--数据结构-区块 

区块头

区块头由三组区块元数据组成。首先是一组引用父区块哈希值的数据,这组元数据用于将该区块与区块链中前一区块相连接。第二组元数据,即难度、时间戳和nonce,与挖矿竞争相关。第三组元数据是merkle树根(一种用来有效地总结区块中所有交易的数据结构)。

数据项 大小(Byte) 存储方式 描述
Version 4 小端 区块版本,规定了区块遵守的验证规则
Previous Block Hash 32 内部字节顺序 上一个区块哈希值(SHA256 (SHA256(Block Header)))
Merkle Root 32 内部字节顺序 Merkle 树根,包含了所有交易的哈希
Timestamp 4 小端 区块产生时间戳,大于前11个区块时间戳的平均值,全节点会拒绝时间戳超出自己2小时的区块
nBitS 4 小端 工作量证明(POW)的目标难度值,
Nonce 4 小端 用于POW的一个随机数,随着算力增大可能会导致Nonce位数不够 协议规定时间戳和CoinbaseTransaction信息可修改用于扩展Nonce位数

参考文章:https://blog.csdn.net/u013137970/article/details/69891985

首先分析前 80 字节的区块头信息:

0100000055bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000ff104ccb05421ab93e63f8c3ce5c2c2e9dbb37de2764b3a3175c8166562cac7d51b96a49ffff001d283e9e70

按照字节长度和字段顺序逐步对 80 字节区块头数据进行分析,为了便于理解,将 json 格式和 hex 格式数据进行对比。比特币的原始数据保存方式是小端格式编码。也就是原始十六进制格式值需要字节逆转转化为大端格式数据然后才能转化为正常的数值。因为大端格式编码就是内存地址大的空间保存高位,书写出来就是左边的数据表示高位,与十进制表示法相同,更符合人的阅读习惯,

例如小端十六进制格式数据为 0x01020304,那么转化为大端正常的数据就是 0x04030201。04代表高位字节值,01 代表低位字节值,转化为十进制数为 4∗166+3∗164+2∗162+1∗160=67305985。

首先4个字节是区块版本号:

    json格式ver字段:1
    hex格式4个字节数据:01000000

将小端十六进制数据0x01000000转化为大端格式数据0x00000001,则转化为十进制数值为1。

其次32个字节是父区块头哈希值:

    json格式prev_block字段:000000002a22cfee1f2c846adbd12b3e183d4f97683f85dad08a79780a84bd55
    hex格式32个字节数据:55bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000

将小端十六进制数据0x55bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000转化为大端格式数据0x000000002a22cfee1f2c846adbd12b3e183d4f97683f85dad08a79780a84bd55,则在验证的时候需要将此数据转化为大数与标准难度值进行比较,最高有效位为0x2a,最低有效位为0x55。

其次32个字节是默克尔树根哈希值:

    json格式mrkl_root字段:7dac2c5666815c17a3b36427de37bb9d2e2c5ccec3f8633eb91a4205cb4c10ff
    hex格式32个字节数据:ff104ccb05421ab93e63f8c3ce5c2c2e9dbb37de2764b3a3175c8166562cac7d

同理,小端十六进制值转化为大端格式的默克尔树根需要将32字节逆序转换。

其次4个字节是时间戳:

    json格式time字段:1231731025
    hex格式4个字节数据:51b96a49

将小端十六进制数据0x51b96a49转化为大端格式数据0x496ab951,则转化为十进制数值为1231731025,表示的是自1970年1月1日0时0分0秒以来的秒数,转化为格林尼治时间为2009-01-12 03:30:25,这说明矿工挖出该区块的时间在这附近。

其次4个字节是难度目标:

    json格式bits字段:486604799
    hex格式4个字节数据:ffff001d

将小端十六进制数据0xffff001d转化为大端格式数据0x1d00ffff,则转化为十进制数值为486604799。

其次4个字节是Nonce:

    json格式nonce字段:1889418792
    hex格式4个字节数据:283e9e70

将小端十六进制数据0x283e9e70转化为大端格式数据0x709e3e28,则转化为十进制数值为1889418792。

class CBlockHeader
{
public:
      // header
    int32_t nVersion; // 版本
    uint256 hashPrevBlock;//前一个区块的hash
    uint256 hashMerkleRoot;// Merkle树根
    uint32_t nTime;// 时间戳
    uint32_t nBits; // POW难度
    uint32_t nNonce;// 要找的随机数

    CBlockHeader()
    {
        SetNull();
    }

    ADD_SERIALIZE_METHODS;

    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);
    }

    uint256 GetHash() const;

    int64_t GetBlockTime() const
    {
        return (int64_t)nTime;
    }
};

区块哈希值

区块哈希值可以唯一、明确地标识一个区块,并且任何节点通过简单地对区块头进行哈希计算都可以独立地获取该区块哈希值,这个值必须满足POW的DifficultyTarget,该区块才被认为有效。

通过SHA256算法对区块头进行二次哈希计算而得到的数字指纹,产生的32字节哈希值被称为区块哈希值

区块体

区块体是交易的集合,Merkle树是一种哈希二叉树,它是一种用作快速归纳和校验大规模数据完整性的数据结构。

class CBlock : public CBlockHeader
{
public:
    // network and disk
    std::vector<CTransactionRef> vtx;// 所有的交易

    // memory only
    mutable bool fChecked; //// 交易是否验证过并构成Merkle树

    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;
};

创世区块

区块链上第一个区块被称为创世区块,它是所有区块的共同祖先。

比特币源代码--12--数据结构-区块