简介:
到目前为止,我们实现了一个简单的区块链基础模型,建立了工作量证明的机制。本文将与大家一起实现区块链的持久化存储,以及实现一个简单的命令行对区块链进行操作。我们先忽略区块链“分布式”的特征,专注实现其数据库的特征。
数据库:
目前为止,我们没有数据库,我们将区块链存在内存中。对于实现的区块,我们再次使用,不能和别人分享该区块,因此我们要实现区块链的持久化。
比特币白皮书中并没有指定特定的数据库类型,它取决于开发者的选择,在实际使用中比特币使用leveldb作为数据库。大家来了解一种新的数据库:
BoltDB:
1.简单
2.使用go语言
3.不需要启动服务
4.满足我们需要的数据结构需求
From the BoltDB’s README on Github:
Bolt is a pure Go key/value store inspired by Howard Chu’s LMDB project. The goal of the project is to provide a simple, fast, and reliable database for projects that don’t require a full database server such as Postgres or MySQL.
Since Bolt is meant to be used as such a low-level piece of functionality, simplicity is key. The API will be small and only focus on getting values and setting values. That’s it.
简单的key/value,api专注于setting和getting。
需要注意的是,boltDB存储二进制类型数据,因此为了存储区块,我们必须进行序列化,我们使用golibarary中的encoding/gob作为序列化和反序列化函数。
数据库结构:
我们参考比特币的存储结构(将区块链数据分为两个bucket):
1.blocks区块存储区块链中区块的元数据。
2.chainstate 存储链的状态,存储未话费交易输出和一些元数据。
在blocks区,其key/value结构如下:
1.'b' + 32位区块哈希 作为区块的索引
2.'f' + 4位文件句柄 -> 文件信息记录
3.'l' -> 4位文件句柄 ->上一个区块的文件信息
4.'R' -> 1位布尔型: 是否在索引中
5.'F' + 1位标记长度 + 标记名 -> 1 byte boolean: various flags that can be on or off
6.'t' + 32位交易哈希 -> 交易记录
在chainstate区,其key/value结构如下:
1.'c' + 32位交易哈希 -> 本次交易的未花费交易输出
2.'B' -> 32位交易哈希: 到目前为止未消费交易的区块哈希
由于,我们本文并不实现交易,同时我们不分开文件存储,所以数据库总的区块结构可以简化为:
1.32位区块哈希>区块结构(序列化的)
2.‘l’>上一个区块的哈希
序列化:
由于boltDB只能存储[]byte类型数据,为了存储区块数据,我们需要实现序列化接口。我们使用go libarary中提供的encoding/gob函数实现区块的序列化。
另外我们需要一个函数,将db中的block反序列化为Block结构,以再次读取。
持久化:
从NewBlockChain函数开始,新建了一个BlockChain实例,并且将GeneiusBlock加入其中。
持久化步骤如下:
1.打开一个数据文件
2.检查是否该文件是否有保存的区块链信息
3.如果存在区块链信息:新建一个区块链实例,设置该区块链tip指向db中存储的区块链的最后一个区块哈希
4.如果不存在区块链:
1.新建区块链实例
2.存储在数据库中
3.将创世区块的哈希作为最后一个区块哈希
4.新建一个区块链实例,tip指向创世区块
同时我们需要更新区块的结构如下:
type BlockChainstruct {
tip []byte
db *bolt.DB
}
接下来我们需要更新AddBlock方法:
现在我们实现了添加新区块的功能,但是出现一个问题,我们无法获取已经保存的区块链的信息。因此,我们需要增加新的功能,从db中读取区块链信息。
查看区块链:
至此为止,所有的区块均存储在数据库中,我们可以重新打开区块链文件新增区块。但是我们缺少一个重要功能,一次浏览每个区块的内容。
BoltDB提供api可以循环一个bucket中内容,但是其key是字节排序的,我们希望按区块添加的顺序依次打印区块信息。由于我们不希望一次将所有区块的信息加载入内存,因此需要实现一个迭代功能。
CLI:
目前,我们实现了NewBlockChain、AddBlock方法,现在就开始实现简单的命令行操作方法吧。
1.blockchain_go AddBlock "A pays B two yuan"
2.blockchain_go PrintChain
所有的命令行操作方法,我们都由CLI struct实现。
该结构的命令行由Run方法维护输入。
其中,os.Args返回参数中,第一个参数返回为可执行命令文件路径:
例如: C://goblock/main.exe addblock -data test
上述命令使用os.Args返回参数数组,os.Args[0]为C://goblock/main.exe,os.Args[1]为addblock,os.Args[2]为data,os.Args[3]为test。具体的使用可查看flag包用法,后文会介绍。
到目前为止,我们实现了命令行向区块db中添加区块,以及查看区块信息的功能。
网友评论