首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

txdb 源码分析系列(二)

本文主要从整体逻辑方面,抽象 txdb 模块的代码构建逻辑。

首先 模块主要是用来实现 和 两个模块的落盘逻辑,所以我们将分为两个大的部分,来对其逻辑一一梳理。

原始数据块

首先,我们通过网络接收到原始块,进行块文件存储。

访问块数据文件

块文件通过以下方式访问:

1. CDiskTxPos:一个 struct, 继承 ,主要有两个参数 和 , 指向一个块在磁盘上的位置的指针(一个文件号和偏移量):

2. CBlockFileInfo:该函数用于执行如下任务:

确定新块是否适合当前文件或需要创建新文件

按块和撤消文件计算总的磁盘使用率

遍历块文件并找到可修剪的文件

数据库条目跟踪每个块文件已经有多少个字节使用,它有多少块,高度的范围是存在的以及日期的范围。

Block index

块索引保存所有已知块的元数据,包括块在磁盘上的存储位置。

对于存储在磁盘上的已知块,区块链是按照树状结构来描述其基于主链的众多分支结构(可能有的分支会很小),从根部的生成区块开始,每个区块可能有多个候选区块作为下一个区块。 blockindex 可能有多个 pprev 指向它,但是它们中至多有一个可以是当前活动分支的一部分。

实际上,LevelDB 的块索引是通过 txdb.h 中定义的 包装类来访问的。 请注意,不同的节点会有略微不同的块树, 重要的是看他们是否认同所在的主链。

存储在数据库中的块在内存中表示为 对象。 这种类型的对象首先在收到 header 后被创建; 代码不会等待收到完整的块。 当通过网络接收到 header时,它们使用 stream 的方式被传输到一个 矢量中,然后对其进行检查。 检出的每个header 都会导致创建一个新的,并将其存储到数据库中。

block index有两个重要的变量

nTx:这个块的交易数量。nTx > 0 表示该块的状态至少为

VALID_TRANSACTIONS。

nChainTx:包括此块在内的链中的交易数量,当且仅当此块及其所有父项的交易可用时,才会设置此值。

因此, 是一个 VALID_TRANSACTIONS 链的简写。 注意,这个信息不能通过块状态枚举来获得。 也就是说,VALID_TRANSACTIONS 只意味着它的父母是 TREE,而 VALID_CHAIN 意味着父母也是 CHAIN。 因此,从某种意义上来说,表达式(nChainTx!= 0)是可以被称为 “VALID_nChainTx = 3.5”的状态的缩写 - 因为它比VALID_TRANSACTIONS更多但是小于VALID_CHAIN。

注意:nChainTx只存储在内存中; 数据库中没有对应的条目

在启动时, 将整个数据库加载到内存中,这只需要几秒钟。

mapBlockIndex (map)

mapBlockIndex 包含所有已知的块(“块”“块索引”)。上面我们提到,由于在收到 header 时就创建了块索引并将其存储在 LevelDB 中,因此在块映射中可能没有收到完整块的块索引,更不用说将其存储到磁盘了。

mapBlockIndex 是没有排序的。只要把它想象成你的块块哈希( LevelDB)在内存中。

mapBlockIndex 是从 LoadBlockIndexGuts 中的数据库初始化的,LoadBlockIndexGuts 在启动的时运行。此后,无论何时通过网络接收到新块,都会更新。

mapBlockIndex 只会增长,它永远不会缩小。 (还要注意,块索引的 LevelDB 包装器不包含从数据库中删除块的功能 - 它的写入函数()只写入数据库。相比之下,chainstate 包装器的写入功能()可以写入和删除。

块( ‘b’ 键)被加载到全局 mapBlockIndex 变量中。 mapBlockIndex 是一个unordered_map,它为整个块树中的每个块保存 CBlockIndex。

block 状态

其中一个关键特征就是它的“验证状态”

验证状态不仅会验证当前块,还会去验证其祖先块。

该块的状态是下面的其中一种:

CDBWrapper

CDBWrapper是一个leveldb的包装函数,无论utxo还是block,均通过它写入leveldb,具体参照下图:

主要有如下参数:

path 系统中存储leveldb数据的位置

nCacheSize 配置各种leveldb缓存设置

fMemory 如果为true,则使用leveldb的内存环境

fWipe 如果为true,则删除所有现有数据

obfuscate 如果为true,则通过简单的XOR存储数据。 如果为false,则与零字节数组进行异或运算

UTXO

访问 UTXO 数据库比块索引复杂得多。 这是因为它的性能对比特币系统的整体性能至关重要。 块索引对于性能来说并不是很关键,因为只有几十万个块,在好的硬件上运行的节点可以在几秒钟内检索并滚动(而且不需要经常这样做)。在UTXO数据库中有数百万个coins,并且必须对每个进入mempool或包含在块中的每个输入的输入进行检查和修改。

在 文件的 1941-1946,我们会发现,utxo数据库在这里被初始化。

上述代码首先初始化一个,它有从LevelDB中加载 coin 的方法。

接下来,初始化pCoinsTip,它是代表活动链状态的高速缓存,并由数据库视图支持。

保存对应于活动链的提示的 UTXO 集合, 检索/刷新到数据库视图。

中的 函数演示了代码如何使用缓存与数据库:

首先,代码在缓存中搜索给定交易ID的硬币 (第1行)

如果找到,它返回“提取”的硬币 (2-3行)

如果不是,则搜索数据库 (第5行)

如果在数据库中找到,它会更新缓存(第7行)

CCoinsViewDBCursor

继承自,专门用来迭代:

CCoinsViewDB

继承自 ,CCoinsView 由 coin 数据库备份(chainstate /),主要与 leveldb 进行交互。它会根据 在 LevelDB 设置的 UTXO, 检索 coins 并且 flush 到 LevelDB 的变化:

CoinEntry

是一个基础结构,服务于:

引用

源码:bitcoin-abc(https://github.com/Bitcoin-ABC/bitcoin-abc)

版本号:v0.16.0

本文由 编写,转载无需授权。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180226G0FTTE00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券