比特币原理及其代码实践3-本地存储


比特币如何存储数据

比特币使用 LevelDB来存储数据,这是一个本地存储的高效的key-value的数据库

Bitcoin Core 使用两个 “空间” 来存储数据:

  1. 其中一个 “空间”是 blocks,它存储了描述一条链中所有块的元数据(区块数据,最新的区块hash等)
  2. 另一个 “空间”是 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

chainstatekey -> 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)相关的东西。最终,我们会用到的键值对有:

  1. 32 字节的 block-hash -> block 结构
  2. 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
}