本篇是"用Go构建区块链"系列的第三篇,主要对原文进行翻译。对应原文如下:
https://jeiwan.cc/posts/building-blockchain-in-go-part-3/
1、介绍
到目前为止,我们已经构建了一个带有工作量证明的区块链,这使得挖矿称为可能。我们的实现越来越接近功能完整的区块链,但它仍然缺乏一些重要功能。今天将开始在数据库中存储区块链,之后我们将制作一个简单的命令行界面来执行区块链操作。本质上,区块链是一个分布式数据库。现在我们将省略"分布式"部分,并专注于"数据库"部分。
2、数据库选择
目前,我们实现的区块链中没有数据库; 相反,我们每次运行程序时都会创建区块并将它们存储在内存中。我们不能重复使用区块链,我们无法与其他人共享,因此我们需要将其存储在磁盘上。
我们需要哪个数据库?实际上,他们中的任何一个都可以。在最初的比特币原始论文中,关于使用某个数据库没有任何说法,因此它完全取决于开发者如何选择。 Bitcoin Core,最初由中本聪发布,也是目前比特币实现的参考版本。它使用LevelDB(尽管它仅在2012年引入客户端)。我们将使用...
3、BoltDB
因为:
1.它简单而很小;
2.它用Go语言实现;
3.它不需要运行服务器;
4.它允许构建我们想要的数据结构。
以下摘自 BoltDB在 Github的README :
Bolt是一个纯Go语言写的Key/Value存储,受到了Howard Chu的LMDB项目的启发。该项目的目标是为不需要完整数据库服务器(如Postgres或MySQL)的项目提供一个简单,快速且可靠的数据库。
由于Bolt旨在用作这种低级别的功能,因此简单性是关键。该API将很小,只专注于获取值和设置值。仅此而已。
听起来非常适合我们的需求!让我们花一分钟审查一下。
BoltDB是一个键/值存储,这意味着没有像SQL RDBMS(MySQL,PostgreSQL等)那样的表,没有行,没有列。相反,数据存储为键值对(如Golang中的map)。键值对存储在桶(bucket)中,这些桶(bucket)用于将类似的键值对进行分组(这与RDBMS中的表类似)。因此,为了获得价值,你需要知道一个桶(bucket)和一个键(key)。
关于BoltDB的一个重要的事情是它没有数据类型:键和值是字节数组(byte arrays)。由于我们将Go结构(也就是区块 Block )存储在其中,因此我们需要将它们序列化,即实现将Go结构转换为字节数组并将其从字节数组恢复的机制。我们将使用 encoding/gob 做这件事情。不过,JSON , XML,Protocol Buffers 等均可使用。我们使用 encoding/gob 是因为它很简单,并且是标准Go库的一部分。
4、数据库结构
在开始实施持久性逻辑之前,我们首先需要决定如何将数据存储在数据库中。为此,我们将介绍比特币核心的做法。
简而言之,Bitcoin Core使用两个"桶(buckets)"来存储数据:
blocks 存储描述链中所有块的元数据。
chainstate 存储链的状态,这是目前所有未使用的事务输出和一些元数据。
另外,区块在磁盘上作为单独的文件存储。这是为了达到性能目的而完成的:读取单个块不需要将全部(或部分)全部加载到内存中。我们不会实现这个。
在 blocks,key -> value 对是:
1.'b' + 32 字节的区块 hash -> 区块索引记录
2.'f' + 4 字节的文件数字 -> 文件信息记录
3.'l' -> 4 字节的文件数字:最后一个使用过的区块文件数字
4.'R' -> 1 字节的布尔值: 我们是否要去重新索引
5.'F' + 1 字节的标志名长度 + 标志名字符串 -> 1 字节布尔值: 开或关的多种标志
6.'t' + 32 字节的交易hash -> 交易索引记录
在 chainstate,key -> value 对是:
1.'c' + 32 字节的交易hash -> 未使用的交易出账记录
2.'B' -> 32 字节的区块hash: 数据库应该表示的未使用交易出账的区块哈希
(详细解释可以在这里找到)
因为我们暂时并没有交易信息,我们可以只有一个 blocks 桶。另外,如上所述,我们将整个数据库存储为单个文件,而不将区块存储在单独的文件中。所以我们不需要任何与文件编号相关的东西。所以这些是我们将要使用的 key -> value(键值)对:
1.32 字节的区块h