比特币源码剖析(四)之核心数据结构

比特币源码剖析(四)之核心数据结构

Bitcoin justnode 10个月前 (03-13) 174浏览

区块的结构

字段 说明 大小
Magic no 魔术字,值为0xD9B4BEF9 4 字节
Blocksize 表示区块的大小,单位是字节 4 字节
Blockheader 包含6个字段:version,hashPrevBlock,hashMerkeRoot,Times,Bits,Nonce 80 字节
Transaction counter 区块中包含的交易数量 1 – 9 字节
transactions 存放交易的列表  

CBlock和CBlockHeader类
CBlock继承自CBlockHeader类,其中CBlockHeader类为区块头中的六个字段定义了六个成员变量,CBlock类定义了成员变量vtx,用于保存区块中的transactions。同时CBlock类中定义了BuildMerkleTree方法用来生成默卡尔树。
CBlockHeader类的定义如下所示,该类中包含6个成员变量。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

class CBlockHeader

{

public:

    // header

    static const int32_t CURRENT_VERSION=4;

    int32_t nVersion;

    uint256 hashPrevBlock;

    uint256 hashMerkleRoot;

    uint32_t nTime;

    uint32_t nBits;

    uint32_t nNonce;

    CBlockHeader()

    {

        SetNull();

    }

};

CBlock类定义如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

class CBlock : public CBlockHeader

{

public:

    // network and disk

    std::vector<CTransaction> vtx;

    // memory only

    mutable std::vector<uint256> vMerkleTree;

    CBlock()

    {

        SetNull();

    }

    uint256 BuildMerkleTree(bool* mutated = NULL) const;

    std::vector<uint256> GetMerkleBranch(int nIndex) const;

    static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex);

    std::string ToString() const;

};

交易的结构

字段 描述 大小
Version no 版本号,值总是为1 4 字节
In-counter input的数量 1 – 9 字节
list of inputs 存放input的列表 不固定
Out-counter output的数量 1 – 9 字节
list of outputs 存放output的列表 不固定
lock_time 交易的锁定时间 4 字节

CTransaction类是比特币交易的核心类,类的定义如下所示,该类中有两个vector变量vin和vout,分别存放CTxIn和CTxOut类型的数据。其中CTxIn是输入交易,CTxOut是输出交易。

CTransaction类的定义如下所示:

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

class CTransaction

{

private:

    /** Memory only. */

    const uint256 hash;

    void UpdateHash() const;

public:

    static const int32_t CURRENT_VERSION=1;

    const int32_t nVersion;

    const std::vector<CTxIn> vin;

    const std::vector<CTxOut> vout;

    const uint32_t nLockTime;

    /** Construct a CTransaction that qualifies as IsNull() */

    CTransaction();

    bool IsNull() const {

        return vin.empty() && vout.empty();

    }

    const uint256& GetHash() const {

        return hash;

    }

    bool IsCoinBase() const

    {

        return (vin.size() == 1 && vin[0].prevout.IsNull());

    }

    std::string ToString() const;

};

CTransaction类中变量的组成如下图所示:

scriptSig和scriptPubKey的作用,请参考理解比特币脚本一文

假设有这么一系列交易:

1. 上图的三个交易都是单输入单输出交易
2. 每个『输入交易』『输出交易』中,都包含对应的『脚本』
3. 交易a,Alice转账给Bob;交易b,Bob转账给Carol;交易c,Carol转账给Dave
4. 当前交易的『输入』都引用前一个交易的『输出』,如交易b的『输入』引用交易a的『输出』

按照之前的说法,交易a中的『输出脚本』就是Alice为Bob出的数学题。那么,Bob想要引用交易a『输出交易』的比特币,就要解开这道数学题。题解是在交易b的『输入脚本』里给出的!Bob解开了这道题,获得了奖金,然后在交易b中为Carol出一道数学题,等待Carol来解…

所以说,下图中相同颜色的『输出』和『输入』才是一对题和解:

scriptPubKey和scriptSig的格式如下:

1

2

scriptPubKey: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

scriptSig: <sig> <pubKey>

验证的时候,scriptSig先把签名和公钥压到栈里,之后OP_DUP复制一份公钥,那么现在栈里就是一个签名,两个公钥。接着用OP_HASH160计算栈顶公钥的hash,再压上一个交易的收款人的公钥hash值到栈,用OP_EQUALVERIFY比较两个hash值,相同就说明这个钱不是你无中生有来的。最后OP_CHECKSIG检查签名,说明这个钱的确是你花出去不是别人花出去的,因为只有有私钥的你才能签名。

下面的步骤形象的展示了验证的过程。每一步中只有一个栈,箭头表示栈状态的变化。

第一步、

第二步、

第三步、

第四步、

第五步、

第四和第五步是用来验证你的公钥是不是出现在别人的vout交易中。如果你的公钥出现在别人的vout中,说明你拥有这部分比特币。

第六步、

这一步是用来验证你的币是你自己花出去的,不是别人花出去的。因为你的公钥只能解密自己私钥加密的内容,而不能解密别人私钥加密的内容。

交易的签名和验证

签名: sign(交易的摘要,发送方私钥) =>  签名

验证:  verify(签名,发送方的公钥) 计算的结果是否等于交易的摘要

交易的发送方对交易进行签名的作用有两个:

1.证明自己认可这笔交易

2.矿工节点需要验证签名和公钥是否匹配,以保证这笔交易是自己花出去的,而不是别人。

参考:https://zhuanlan.zhihu.com/p/25461051

btc_parser

解析比特币的块文件的工具

钱包目录结构

块文件结构

在**.dat文件中,存储了比特币区块链的信息

比特币的块结构如下

大小(字节) 名称 数据类型 描述
4 magic_number uint32 总是0xD9B4BEF9,作为区块之间的分隔符
4 block_size uint32 后面数据到块结束的字节数
80 block_header char[] block header
varies transaction_cnt uint 交易数量
varies transaction char[] 交易详情

block header的结构如下

大小(字节) 名称 数据类型 描述
4 version int32_t 版本号
32 previous_block_hash char[32] 前一个block的hash值
32 merkle_root_hash char[32] 区块内所有交易的merkle hash值
4 time uint32 unix时间戳,矿工挖矿的时间
4 nBits uint32 该块的标题hash必须小于的值。难度
4 nonce uint32 随机值,用于产生满足难度的hash值

交易的结构如下

大小(字节) 名称 数据类型 描述
4 version uint32 交易版本号
varies tx_in_count uint 交易输入数量
varies tx_in tx_in 交易输入
varies tx_out_count uint 交易输出数量
varies tx_out tx_out 交易输出
4 lock_time uint32 锁定时间

交易输入的结构如下

大小(字节) 名称 数据类型 描述
32 previous_output_hash outpoint 前置交易hash
4 previous_output_index uint32 前置交易index
varint script_bytes uint 解锁脚本长度
varies signature_script char[] 解锁脚本
4 sequence uint32 序列号

交易输出的结构如下

大小(字节) 名称 数据类型 描述
8 value int64 花费的数量,单位是聪
1+ pk_script_size uint pubkey脚本中的字节数量
varies pk_script char[] 花费这笔输出需要满足的条件

猜你喜欢

转载自blog.csdn.net/TuxedoLinux/article/details/86255561