区块Block

比特币源码研读(2)数据结构-区块Block

上一篇基本认识了bitcoin源码结构和个模块代码的功能,今天看区块。

区块是组成区块的基本单位,我们可以通过bitcoin-cli命令查看一个区块的基本信息。

bitcli-cli获取区块信息

接下来我们就在源代码找一下区块的定义,由于我们并不知道区块定义在哪。我们试着全局搜一下block.h(或block.cpp):

搜索block.h

进去发现还真被我找到了,其实我们在上一篇的bitcoin源码结构的目录结构里已经说过./private目录下是区块类和交易类的实现。接下来,就让我们一窥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.
 *
 **网络中的节点不断收集新的交易打包到区块中,所有的交易会通过两两哈希的方式形成一个Merkle树
 * 打包的过程就是要完成工作量证明的要求,当节点解出了当前的随机数时,
 * 它就把当前的区块广播到其他所有节点,并且加到区块链上。
 * 区块中的第一笔交易称之为CoinBase交易,是产生的新币,奖励给区块的产生者  
 * 
 * add by chaors 20180419
 */

class CBlockHeader
{
public:
    // header
    int32_t nVersion;       //版本
    uint256 hashPrevBlock;  //上一个区块的hash
    uint256 hashMerkleRoot; //包含交易信息的Merkle树根
    uint32_t nTime;         //时间戳
    uint32_t nBits;         //工作量证明(POW)的难度
    uint32_t nNonce;        //要找的符合POW的随机数

    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);     //难度为0说明区块还未创建,区块头为空
    }

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

    int64_t GetBlockTime() const //获取区块时间
    {
        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.
 *
 **描述区块链中在其他节点的一个位置,
 *如果其他节点没有相同的分支,它可以找到一个最近的中继(最近的相同块)。
 *更进一步地讲,它可能是分叉前的一个位置
 */
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函数

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

std::string CBlock::ToString() const    //区块对象格式化字符串输出
{
    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();
}

区块结构分析

区块是通过前后链接构成区块链的一种容器数据结构。它由描述区块主要信息的区块头和包含若干交易数据的区块共同组成。区块头是80字节,而平均每个交易至少是250字节,而且平均每个区块至少包含超过500个交易。所以,一个区块是比区块头大好多的数据体。这也是比特币验证交易是否存在采用Merkcle树的原因。

整体结构

数据项大小(Byte)描述
Block Size4区块大小
Block Header80区块头信息大小
Transactionsm*n(n>=250)所有交易的列表
Transactions Counter1-9交易数额

比特币的区块大小目前被严格限制在1MB以内。4字节的区块大小字段不包含在此内!

区块头

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

区块标识符

  • BlockHash 区块哈希值,是通过SHA256算法对区块头信息进行哈希得到的,这个值必须满足POW的DifficultyTarget,该区块才被认为有效。同时,也是区块的唯一标识符,可以通过通过bitcoin-cli根据BlockHash查询区块信息(文章开头我们就使用过)

  • BlockHeight 区块高度,是用来标示区块在区块链中的位置。创世区块高度为0,每一个加在后面的区块,区块高度递增1。可以通过bitcoin-cli根据高度查询区块哈希值(文章开头我们就使用过)

注:BlockHeight并不是唯一标识符,当区块链出现临时分叉时,会有两个区块的高度值相同的情况。

创世区块

区块链上第一个区块被称为创世区块,它是所有区块的共同祖先。我们可以查看下比特币的创世区块:

创世区块

比特币创始人聪哥在创世区块包含了一个隐藏的信息。在其Coinbase交易的输入中包含这样一句话“The Times 03/Jan/2009 Chancellor on brink of second bailout forbanks.”这句话是泰晤士报当天的头版文章标题,聪哥这样做的目的不得而知。但是,这样一条非交易信息可以轻而易举地插入比特币,这个现象值得深思。如此,就不难理解前不久曝光的"不法分子利用比特币存储儿童色情内容"新闻,当然这种存储可能远比聪哥的那句话要更复杂一点。

思考

    1. 我们查看的区块信息中,以下不在源码结构中的字段有什么含义?为什么他们不在源码定义的区块结构中?
      confirmations
      strippedsize
      weight
      mediantime
      bits
      chainwork
  • 2.为什么区块的哈希没有定义在区块头内部?

注:以上问题可能我也没答案,可以留言我们一起交流共同进步。

对区块的认识就告一段落,下一篇准备去探索比特币数据结构-交易的结构。

参考文献

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,第二个引用是比特币的创世区块中的信息,而与Python无关。而第一个引用则是安装Python和pip的命令。因此,我猜测您想要了解的是Python中的创世区块。 Python中的创世区块是指在区块链中的第一个区块,它是由区块链的创建者手动创建的。在Python中,我们可以使用以下代码来创建一个简单的创世区块: ```python import hashlib import json from time import time class Blockchain(object): def __init__(self): self.chain = [] self.current_transactions = [] # Create the genesis block self.new_block(previous_hash='1', proof=100) def new_block(self, proof, previous_hash=None): """ Create a new Block in the Blockchain :param proof: <int> The proof given by the Proof of Work algorithm :param previous_hash: (Optional) <str> Hash of previous Block :return: <dict> New Block """ block = { 'index': len(self.chain) + 1, 'timestamp': time(), 'transactions': self.current_transactions, 'proof': proof, 'previous_hash': previous_hash or self.hash(self.chain[-1]), } # Reset the current list of transactions self.current_transactions = [] self.chain.append(block) return block def new_transaction(self, sender, recipient, amount): """ Creates a new transaction to go into the next mined Block :param sender: <str> Address of the Sender :param recipient: <str> Address of the Recipient :param amount: <int> Amount :return: <int> The index of the Block that will hold this transaction """ self.current_transactions.append({ 'sender': sender, 'recipient': recipient, 'amount': amount, }) return self.last_block['index'] + 1 @staticmethod def hash(block): """ Creates a SHA-256 hash of a Block :param block: <dict> Block :return: <str> """ # We must make sure that the Dictionary is Ordered, or we'll have inconsistent hashes block_string = json.dumps(block, sort_keys=True).encode() return hashlib.sha256(block_string).hexdigest() @property def last_block(self): return self.chain[-1] ``` 这是一个简单的区块链实现,其中包括创建创世区块的代码。在这个实现中,我们使用了Python的哈希库和JSON库来创建和管理区块链。您可以根据需要进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值