主页 > imtoken苹果版最新版 > 深入理解比特币数据存储
深入理解比特币数据存储
当我们打开bitcoincash目录,我们会看到如下文件目录。 这些文件是什么,它们存储在什么地方? 下面我们就一一揭开它的神秘面纱。
比特现金文件夹:
比特现金.png
块文件夹:
方块.png
索引文件夹:
索引.png
链态文件夹:
链状态.png
通过观察bitcoincash目录,我们可以发现比特币一共存放了以下内容:
其中,块数据和块undo数据直接存储在磁盘上,块索引数据和utxo数据写入leveldb数据库。
水平数据库
为了便于理解leveldb的目录存储结构,下面简单介绍一下leveldb的原理。
leveldb.png
leveldb采用了LSMTree的存储结构,其存储逻辑大致如上图所示。 具体步骤如下:
将一条数据写入leveldb时,会先将数据写入日志文件,日志文件完成后,再将数据写入内存(memtable)。 当memtable中的数据已满时,.log文件会被锁定,同时会生成一个Immutable table文件。 该文件只支持读取操作比特币是怎么存储的,不支持写入和删除。 这时会重新生成.log文件和memtable文件,新的write输入一条数据时,会重写空的.log文件和memtable。 LevelDb后台调度会将Immutable Memtable的数据导出到磁盘,形成一个新的SSTable文件。 SSTable是在内存中不断导出数据并进行Compaction操作形成的,SSTable中的所有文件都是层次结构,第一层为Level 0,第二层为Level 1,依此类推,层级逐渐递增。 这也是它被称为 LevelDb 的原因。 各文件含义: 当前文件:
当前文件有什么用? 这个文件的内容只有一个信息,就是记录当前manifest文件的名称。 因为在LevleDb运行过程中,随着Compaction的进行,SSTable文件会发生变化,会产生新的文件,旧的文件会被丢弃。 清单也将反映这一变化。 这时候往往会生成一个新的Manifest文件来记录这个变化,Current用来指出哪个Manifest文件是我们关心的Manifest文件。
清单文件
Manifest文件存放的是xxx.ldb文件的元数据信息,因为我们只有xxx.ldb文件,不知道它属于哪一层。 这也是Manifest文件的作用。 每次打开 DB 时,leveldb 都会创建这样一个文件,并在其末尾附加一个后缀标识符。 该文件以附加模式写入磁盘。
日志文件
leveldb运行时的日志文件,方便用户查看。
锁定文件
它是一个使用文件实现的DB锁,它告诉用户一个leveldb的实例在一个进程内只允许被打开一次。
xxx.ldb文件
这个文件是记录leveldb的数据文件(区别于元数据文件),以KV的有序形式写入数据库。
level-0的文件大小为压缩后memtable文件的大小,level-1为10MB,level-2为100MB,level-3为1000MB,以此类推。
xxx.log文件
上面我们说了,为了保证数据不丢失,在写入数据之前会写入.log文件。 .log文件存储了最近的一系列更新,每一次更新都以append的形式追加到当前日志文件中。 当日志文件达到 4MB 时,将转换为有序文件,并创建一个新的日志文件来记录最新的更新。 这个日志文件映射到上面提到的 memtable 文件。 当memtable文件写入level-0时,对应的日志文件会被删除,并重新创建一个新的日志文件来对应新的memtable。 等等。
综上所述,我们可以看出leveldb是一种典型的数据和元数据分离存储的存储模型数据库。
链态文件夹
chainstate是一个leveldb数据库,主要存储utxo和tx的一些元数据信息。 chainstate 中存储的数据主要用于验证新进入的区块和 tx 是否合法。 如果没有这个操作,就意味着你需要执行全表扫描来验证每一个花出去。
chainstate_file.png
如上图所示,utxo数据主要存放在chainstate文件目录下。 由于需要存储在leveldb中,所以必须以key和value的格式准备数据。
coincoin.pngcoin_db_key.png
如上图:key一共包含三部分,1个字节的大写C,32个字节的hash,4个字节的序号。
value为coin的序列化值,如下:
图片.png
coin还包含txout结构,如下:
图片.png
对nValue和scriptPubKey分别使用不同的压缩方式进行序列化,如下:
image.png 最佳 blockimage.png
比特币在chainstate中还记录了另一部分信息,首先判断当前区块的hash是否为null,如果不为null,则以1字节的大写B为key,32字节的block hash为value,将其写入数据库中的硬币。
总结:utxo写入磁盘的数据库是:chainstate,写入的数据分为两部分。 第一部分:key为outpoint,由+组成,其中txid为32字节,tx out index用var int编码序列化,value为coin序列化后的大小。 第二部分:写入的key是1字节的DB_BEST_BLOCK标识,value是32字节的块哈希。
图片.png
在bitcoin core 0.17中,改变了chainstate目录,多写入了一些数据,如下图:
image.png注意:
在0.17的结构中,第一部分不会长期存在,只会在触发BatchWrite的第一步写入,整个coinsmap写入完成后,这部分将被删除。
索引文件夹:image.png
index文件夹主要记录block的索引信息。 区块索引是区块的元数据信息,包括区块头信息、高度、链信息; 按照utxo存储的思路,我们会在blocks key和value中寻找索引。
重新索引图像.png
写入索引的第一部分数据:key是一个1字节的DB_REINDEX_FLAG,value是一个1字节的布尔值。 用于标识是否需要重建索引操作。
txindeximage.png
index中写入的第二部分数据:key是1字节的DB_TXINDEX加上32字节的hash,value是序列化后的CDiskTxPos,它唯一的成员是int类型的nTxOffset。 这些是可选的,只有在启用“txindex”时才会出现。 每个记录存储:
交易存储在哪个块文件号中。 交易属于哪个文件,偏移量存储在哪个文件中。从块的开头到交易本身存储位置的偏移量。 块文件信息图像.png
第三部分写入索引的数据:这部分数据比较重要,
文件信息
先写入fileinfo数据,key是1字节的DB_BLOCK_FILES加上4字节的文件号,value是CBlockFileInfo序列化后的数据。
最后一个文件
接下来写入lastFile信息,key为1字节DB_LAST_BLOCK,value为4字节nLastFile。
块索引
最后写入blockindex信息,key是1字节的DB_BLOCK_INDEX加上32字节的blockhash值就是CDiskBlockIndex的序列化数据。
国旗图片.png
index中写入的第四部分数据:key是1个字节的DB_FLAG加上标志名,value是1个字节的布尔值(1为真,0为假),可以打开或关闭各种类型的标志,例如当前defined:TxIndex(是否开启交易索引)。
image.pngblock 文件夹
block文件夹下主要有两种文件,一种是blk???.dat,用来存放blocks比特币是怎么存储的,另一种是rev???.dat,用来存放undo blocks。 主要存储格式如下:
存储块序列化数据。
图片.png
存储格式如下(按顺序):
image.pngMessageStart
MessageMagic是在启动程序的时候定义的,在不同的网络中定义不同。 MessageMagic分为netMagic和diskMagic:
主网:image.pngTestNet:image.pngRegTestNet:image.png
MessageMagic是一个4字节数组,写入数据时调用FLATDATA宏定义,如下:
图片.png
FLATDATA会将vector或map数据结构中的元素按照数组的原始顺序转储到磁盘中。
图片.png
write()函数第一个参数表示要写入数据的起始位置,第二个参数表示要写入数据的大小,pbegin指向vector的起始位置,pend指向的位置结束元素+1,所以这里先写入4字节的messageStart。
image.pngBlockSize
BlockSize主要描述了Block序列化后的长度,为4字节。
image.png块序列化
区块序列化主要序列化两部分,一部分是BlockHeader结构,另一部分是交易的共享指针vtx:
图片.png
第一部分是 BlockHeader:
图片.png
第二部分是vtx:
图片.png
CTransaction主要序列化以下内容:
image.png总结:
blk????.dat文件先写入4字节的messageMagic,再写入4字节的block size,最后写入block的序列化数据。
存储 undoblock 序列化数据。
图片.png
MessageStart 和 UndoBlockSize 与 Block 中相同。
BlockUndo序列化
BlockUndo序列化只有一个vtxundo对象,vtxundo是CTxUndo的一个vector,其序列化操作如下:
图片.png
CTxUndo的序列化操作如下,其中prevout为Coin的向量:
图片.png
Coin的序列化操作如下:
图片.png
Coin由两部分组成,代码如下:
图片.png
TxOut的序列化如下,nValue和scriptPubKey的序列化采用不同的压缩方式:
image.pngBlockUndoCheckSum
具体代码如下:
图片.png
将hashBlock和blockundo的数据写入CHashWriter的接口,得到CHashWriter的hash,将32字节的hash值写入undofile文件。
blk????.dat和rev????.dat存放的数据不同,block存放的是block header和txs的序列化数据,undo block存放的是txout的序列化数据。
关于文件大小的一些问题:
blk.dat默认初始化大小为16M,最大128M,rev.dat默认初始化大小为1M。
图片.png
导入块时会检查磁盘空间,必须大于50M,否则磁盘空间不足
图片.png
关于剪枝,磁盘要求必须大于550M:
图片.png
比特币需要保留 288 个区块。 按照每个块1M的大小计算,需要288M,另外还需要15%的空间来存放UNDO数据,加上20%的孤块率,大约需要397M的空间,这是最小的,但是我们还需要加上同步块的数据blk.dat,需要128M,再加上15%左右的undo数据,大概147M。 所以整体需要147M+397M=544M,所以设置限制为550M。
如何实现比特币的热存储和冷存储?
要理解热存储和冷存储,我举个例子。 如果张三手里有10万元,那么张三可能会拿出5000元日常开销,剩下的95000元存入银行。 那么5000对应的是热库,95000对应的是冷库。
冷库是对比特币的再次保险,就像银行里的现金一样。 在冷存储中,可以离线生成私钥(在没有联网的机器上),然后离线保存,离线使用。 有一个叫做 Armory 的客户端可以做到这一点。 因为没有互联网,即使是最强大的黑客也无法窃取您的比特币。
比特币脚本是如何应用的?
比特币中也有一个比特币脚本。 比特币使用这个脚本来验证交易的合法性,而不是需要人工验证。
脚本相当于编写了一堆命令参数。 在发起交易时,需要将收款人的公钥以脚本的形式写入,然后脚本会验证交易的合法性,让收款人使用自己的私钥完成脚本验证。 它使用栈的数据结构。 这个结构从技术上解释了,一般来说可以push,pop,检查。 就像把书放在一个有一定高度的盒子里,盒子的宽度只能装一本书。 第一个进去的一定是最后一个出去的。 因为使用脚本,比特币实际上是通过计算机来实现所有数字货币的转账机制。
#欧易OKEx##比特币[超话]##数字货币#