Bitcoin blk*.dat文件分析

存储数据的文件

Bitcoin 的原始数据可以在$HOME/.bitcoin/blocks 下面找到。这个目录下面主要有两类文件,一种是类似于这样的文件:blk00952.dat, 而另一种是类似于这样的文件:rev000952.dat。 前一种 blk 开头的文件就是存储 Bitcoin 源数据的文件,而后一种是用来做 rewind 的。每一个 blk 数据文件大小的上限都是 128MB,但并不是非常精确地小于128M,也有可能达到130多MB。当一个文件写满后,Bitcoin 程序会新建一个类似的文件来存储新收到的区块信息。

让我们先通过如下命令,来看看这些文件里面都存了什么。找到Bitcoin的区块数据文件, 一般在blocks文件下面,我们以blk00000.dat文件为例,在命令行中输入如下命令:

od -x –endian=big -N 297 -An blk00000.dat

数据显示截图如下:
《Bitcoin blk*.dat文件分析》

上面的文件就是 Bitcoin 的 Genesis BLock。它是二进制数据(以 16 进制表示),每两个字符表示一个字节。下面我们进一步了解其中的数据结构.

数据的存储结构

这里有一个直观的图表对比特币数据结构进行描述.
《Bitcoin blk*.dat文件分析》

在上图中,蓝色代表复合数据类型,橙色代表简单数据类型,绿色是注解。一个 Block 由一个 Block Header,Number of Transactions 和若干个 Transaction 组成。每个 Transaction 又由几个简单数据类型和若干个 Transaction Input/Ouput 组成。以此类推,这里做几点说明:

  • 几乎所有数据都是以小端类型(注:Little-Endian,下同)存储的,但是 Transaction Input/Output 里的script除外。它们是以大端方式(注:Big-Endian,下同)存储的。
  • magic number是一个固定的数,其值为 0xD9B4BEF9, 为什么叫做magic number ? 这是因为比特币源码中给这四个字节数据的变量名称叫做magic number.
  • variable integer本身的长度不定,在 1 到 9 字节之间。具体的解析规则如下:
    • 如果第一个字节小于 253,那么直接返回该字节所代表的数值.
    • 如果第一个字节等于 253,那么往后再读一个字节作,将其值作为返回值
    • 如果第一个字节等于 254,那么往后再读四个字节作,将其值作为返回值
    • 如果第一个字节等于 255,那么往后再读八个字节作,将其值作为返回值
      如果我们把之前的数据按照上述规则带入的话可以得到如下图表。
      《Bitcoin blk*.dat文件分析》怎么样,现在是不是清楚多了?注意,我并有没把数据换成大端格式,请自行转换。

计算额外字段

细心的同学可能已经注意到了,这些数据中不包括几个非常重要的字段,Block Hash,Block Height,Transaction Hash 以及 Addresses。不错,中本聪在设计 Bitcoin 的时候可谓是极度的“抠门”,能通过计算得出的数据绝对不存在链上。从链的设计角度来看,这样做是绝对正确的,它能节省许多磁盘空间,但是这却给我们带来了额外的工作。下面我们分别谈谈各个字段的计算。

计算 Block Hash 以及 Transaction Hash

Block 和 Transaction 的 Hash 值都是由相同的算法得出的,不同点只是在于参与计算的数据不同。对于 Block Hash 来讲,我们的输入数据只是 80 字节的 Block Header,而对于 Transaction 来讲是整个 Transaction 的数据。Block Header 里面是包括 Merkle Root 的,所以在计算 Block Hash 的时候不需要整个 Block 最为输入。

Hash 值是由输入数据进行两次 sha-256 运算得出的,其形式如下:

hash = sha256(sha256(data))

我们分别将 Block Header 和整个 Transaction 带入上述公式便能得到相应的 Hash 值。

计算 Block Height

我们知道,Bitcoin 的原始数据是存储在 blkxxxxx.data 中的,每个 data 文件中都存放了许多个 Block。如果我们按顺序从第 0 字节开始读,把其中的所有 Block 都解析出来,我们会发现这些 Block 并不是完全有序的。换句话说你有可能先读出第 178 个 Block,然后才读出第 177 个 Block。至于为什么会这样,这是因为比特币系统运行的时候,系统下载区块的时候并不是按照区块号从小到大进行下载,而是按照滑动窗口进行下载,每次从一个节点处下载1024个区块, 也从其他节点处下载区块,不同的区块到达本地的时间不同,但是本地存储的时候直接按照顺序存储, 而每个区块所在的文件编号以及在文件中的偏移则存储在leveldb数据库中,具体来说,就是存储在blocks文件下的index下,这里面存储了所有区块的存储信息.

所以想要得到正确的 Block Height, 我们就必须对读出来的、无序的 Block 进行排序。了解 Blockchain 的同学应该知道,Blockchain 的数据结构是一个倒过来的单链表,我们可以通过 Block Hash 和 Previous Block Hash 来将这些 Block 重新串联起来。Genesis Block 的 Previous Block Hash 固定为全 0。这样一来我们也就可以得出 Block Height 了。

计算 Addresses

想要支持最开始的那种根据地址查询 Transaction 的功能,我们就必须求出每笔交易的发款人和收款人地址。这两个数据分别蕴含在 Transaction Input 和 Output 的script字段里面。求出地址的方法大致分为两步:

  1. 对于 Transaction Output,找出其中的 public key 或 public key hash,再根据一定的算法计算出地址。

  2. 对于 Transaction Input, 从其对应的 Previous Transaction Output 中取出地址,作为它的地址。

  3. 我想对第二点进行一些补充说明。虽然在 Transaction Input 里面你能拿到 public key,但是我不建议大家直接从 Transaction Input 里面计算地址。这是因为经过隔离见证之后,计算地址的方式发生了变化,直接从 Input 中解析地址很有可能得到一个与 Previous Transaction Output 不一样的地址。简单来说就是,你有可能以张三的名义收了一笔钱,却用李四的名义花掉了这笔钱。张三李四本来是同一个人,但是因为这个错误,计算账户余额的时候就会出现余额为负数的情况。 ## 结语 中本聪作为 Blockchain 之父,在设计 Bitcoin 的时候有许多奇思妙想,他对那些细节的把控真是让人叹为观止。在这个项目中,如果各位有时间,不妨写一些代码试着解析一下原始数据,这对彻底理解 Bitcoin 有很大的帮助。

PS: 这个解析的方法同样适用于Dash币中的区块分析.

    原文作者:落霞与孤鹜亓飞
    原文地址: https://blog.csdn.net/t46414704152abc/article/details/90174727
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞