比特币如何存储数据
比特币使用 LevelDB来存储数据,这是一个本地存储的高效的key-value的数据库
Bitcoin Core 使用两个 “空间” 来存储数据:
- 其中一个 “空间”是 blocks,它存储了描述一条链中所有块的
元数据(区块数据,最新的区块hash等)
- 另一个 “空间”是 chainstate,存储了一条链的状态,
也就是当前所有的未花费的交易输出
,和一些元数据
在 blocks 中,key -> value 为:
key | value |
---|---|
b + 32 字节的 block hash |
block index record |
f + 4 字节的 file number |
file information record |
l + 4 字节的 file number |
the last block file number used |
R + 1 字节的 boolean |
是否正在 reindex |
F + 1 字节的 flag name length + flag name string |
1 byte boolean: various flags that can be on or off |
t + 32 字节的 transaction hash |
transaction index record |
在 chainstate,key -> value 为:
key | value |
---|---|
c + 32 字节的 transaction hash |
unspent transaction output record for that transaction |
B** |
32 字节的 block hash: the block hash up to which the database represents the unspent transaction outputs |
详情可见 这里。
简单实现
因为目前还没有交易,所以我们只需要 blocks 。另外,正如上面提到的,我们会将整个数据库存储为单个文件,而不是将区块存储在不同的文件中。所以,我们也不会需要文件编号(file number)相关的东西。最终,我们会用到的键值对有:
32 字节的 block-hash
-> block 结构l
-> 链中最后一个块的 hash
为方便演示,我们使用json存储数据
区块链数据结构更新
//区块链
type Blockchain struct {
//map[区块的hash] = 区块
DB map[string]Block
// l 存最新的区块hash值
L string
}
//创建区块链
func CreateBlockchain() *Blockchain {
if !PathExists(dbFile) {
//如果文件不存在,则创建一个blockchain的json文件
f, err := os.Create(dbFile)
}
//创世区块
block := NewGenesisBlock()
var db map[string]Block = map[string]Block{}
hash := hex.EncodeToString(block.Hash)
db[hash] = *block
//新建blockchain对象
bc := &Blockchain{
DB: db,
L: hash,
}
//写入文件
data, _ := json.Marshal(bc)
err := ioutil.WriteFile(dbFile, data, 0666)
return bc
}
//添加区块
func (bc *Blockchain) AddBlock(data string) {
//最新的区块
prevBlock := bc.DB[bc.L]
//新建区块
newBlock := NewBlock(data, prevBlock.Hash, prevBlock.Height+1)
hash := newBlock.GetHashString()
bc.DB[hash] = *newBlock
//更新blockchain指针L
bc.L = hash
//保存
bc.SaveBlockchain()
}
遍历区块链
//区块链迭代器
//我们不会一次性加载所有的区块数据
//而是一个一个的迭代加载
type BlockchainInterator struct {
currentHash string
DB map[string]Block
}
//返回当前块,并指向上一个区块
func (i *BlockchainInterator) Next() *Block {
//返回当前块
block := i.DB[i.currentHash]
//并指向前一个块
i.currentHash = block.GetPrevHashString()
return &block
}