tendermint/blockchain/store.go

269 lines
8.0 KiB
Go
Raw Normal View History

package blockchain
2014-08-10 16:35:08 -07:00
import (
"bytes"
"encoding/json"
"fmt"
"io"
2016-12-19 21:45:45 -08:00
"sync"
2014-08-10 16:35:08 -07:00
2017-11-30 11:08:38 -08:00
wire "github.com/tendermint/go-wire"
2018-01-03 02:29:19 -08:00
2017-10-03 15:49:20 -07:00
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
2018-01-03 02:29:19 -08:00
"github.com/tendermint/tendermint/types"
2014-08-10 16:35:08 -07:00
)
/*
2017-10-04 12:04:23 -07:00
BlockStore is a simple low level store for blocks.
2014-12-23 01:35:54 -08:00
There are three types of information stored:
- BlockMeta: Meta information about each block
- Block part: Parts of each block, aggregated w/ PartSet
2016-04-02 09:10:16 -07:00
- Commit: The commit part of each block, for gossiping precommit votes
2014-12-23 01:35:54 -08:00
2015-06-05 14:15:40 -07:00
Currently the precommit signatures are duplicated in the Block parts as
2016-04-02 09:10:16 -07:00
well as the Commit. In the future this may change, perhaps by moving
2017-10-04 12:04:23 -07:00
the Commit data outside the Block. (TODO)
2015-06-16 21:16:58 -07:00
2017-10-23 16:46:14 -07:00
// NOTE: BlockStore methods will panic if they encounter errors
// deserializing loaded data, indicating probable corruption on disk.
2014-08-10 16:35:08 -07:00
*/
type BlockStore struct {
2016-12-19 21:45:45 -08:00
db dbm.DB
2016-12-22 18:51:58 -08:00
mtx sync.RWMutex
height int64
2014-08-10 16:35:08 -07:00
}
// NewBlockStore returns a new BlockStore with the given DB,
// initialized to the last height that was committed to the DB.
func NewBlockStore(db dbm.DB) *BlockStore {
2014-12-23 01:35:54 -08:00
bsjson := LoadBlockStoreStateJSON(db)
2014-08-10 16:35:08 -07:00
return &BlockStore{
height: bsjson.Height,
db: db,
}
}
2017-10-04 12:04:23 -07:00
// Height returns the last known contiguous block height.
func (bs *BlockStore) Height() int64 {
2016-12-22 18:51:58 -08:00
bs.mtx.RLock()
defer bs.mtx.RUnlock()
2014-08-10 16:35:08 -07:00
return bs.height
}
2017-10-04 12:04:23 -07:00
// GetReader returns the value associated with the given key wrapped in an io.Reader.
// If no value is found, it returns nil.
// It's mainly for use with wire.ReadBinary.
func (bs *BlockStore) GetReader(key []byte) io.Reader {
bytez := bs.db.Get(key)
if bytez == nil {
2014-08-10 16:35:08 -07:00
return nil
}
return bytes.NewReader(bytez)
}
2017-10-04 12:04:23 -07:00
// LoadBlock returns the block with the given height.
// If no block is found for that height, it returns nil.
func (bs *BlockStore) LoadBlock(height int64) *types.Block {
2015-11-10 13:10:43 -08:00
var n int
2014-10-18 01:42:33 -07:00
var err error
2015-01-07 01:15:39 -08:00
r := bs.GetReader(calcBlockMetaKey(height))
if r == nil {
2015-06-16 21:16:58 -07:00
return nil
2015-01-07 01:15:39 -08:00
}
2017-02-14 12:33:14 -08:00
blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
if err != nil {
2017-10-03 15:49:20 -07:00
cmn.PanicCrisis(cmn.Fmt("Error reading block meta: %v", err))
}
bytez := []byte{}
2017-02-14 12:33:14 -08:00
for i := 0; i < blockMeta.BlockID.PartsHeader.Total; i++ {
part := bs.LoadBlockPart(height, i)
bytez = append(bytez, part.Bytes...)
}
2015-11-10 13:10:43 -08:00
block := wire.ReadBinary(&types.Block{}, bytes.NewReader(bytez), 0, &n, &err).(*types.Block)
2014-10-18 01:42:33 -07:00
if err != nil {
2017-10-03 15:49:20 -07:00
cmn.PanicCrisis(cmn.Fmt("Error reading block: %v", err))
2014-10-18 01:42:33 -07:00
}
return block
2014-08-10 16:35:08 -07:00
}
2017-10-04 12:04:23 -07:00
// LoadBlockPart returns the Part at the given index
// from the block at the given height.
// If no part is found for the given height and index, it returns nil.
func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
2015-11-10 13:10:43 -08:00
var n int
var err error
2015-01-07 01:15:39 -08:00
r := bs.GetReader(calcBlockPartKey(height, index))
if r == nil {
2015-06-16 21:16:58 -07:00
return nil
2015-01-07 01:15:39 -08:00
}
2015-11-10 13:10:43 -08:00
part := wire.ReadBinary(&types.Part{}, r, 0, &n, &err).(*types.Part)
if err != nil {
2017-10-03 15:49:20 -07:00
cmn.PanicCrisis(cmn.Fmt("Error reading block part: %v", err))
}
return part
}
2017-10-04 12:04:23 -07:00
// LoadBlockMeta returns the BlockMeta for the given height.
// If no block is found for the given height, it returns nil.
func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
2015-11-10 13:10:43 -08:00
var n int
var err error
2015-01-07 01:15:39 -08:00
r := bs.GetReader(calcBlockMetaKey(height))
if r == nil {
2015-06-16 21:16:58 -07:00
return nil
2015-01-07 01:15:39 -08:00
}
2017-02-14 12:33:14 -08:00
blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
if err != nil {
2017-10-03 15:49:20 -07:00
cmn.PanicCrisis(cmn.Fmt("Error reading block meta: %v", err))
}
2017-02-14 12:33:14 -08:00
return blockMeta
}
2017-10-04 12:04:23 -07:00
// LoadBlockCommit returns the Commit for the given height.
// This commit consists of the +2/3 and other Precommit-votes for block at `height`,
// and it comes from the block.LastCommit for `height+1`.
// If no commit is found for the given height, it returns nil.
func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
2015-11-10 13:10:43 -08:00
var n int
var err error
2016-04-02 09:10:16 -07:00
r := bs.GetReader(calcBlockCommitKey(height))
2015-01-07 01:15:39 -08:00
if r == nil {
2015-06-16 21:16:58 -07:00
return nil
2015-01-07 01:15:39 -08:00
}
2016-04-02 09:10:16 -07:00
commit := wire.ReadBinary(&types.Commit{}, r, 0, &n, &err).(*types.Commit)
if err != nil {
2017-10-03 15:49:20 -07:00
cmn.PanicCrisis(cmn.Fmt("Error reading commit: %v", err))
}
2016-04-02 09:10:16 -07:00
return commit
}
2017-10-04 12:04:23 -07:00
// LoadSeenCommit returns the locally seen Commit for the given height.
// This is useful when we've seen a commit, but there has not yet been
// a new block at `height + 1` that includes this commit in its block.LastCommit.
func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit {
2015-11-10 13:10:43 -08:00
var n int
var err error
2016-04-02 09:10:16 -07:00
r := bs.GetReader(calcSeenCommitKey(height))
if r == nil {
2015-06-16 21:16:58 -07:00
return nil
}
2016-04-02 09:10:16 -07:00
commit := wire.ReadBinary(&types.Commit{}, r, 0, &n, &err).(*types.Commit)
if err != nil {
2017-10-03 15:49:20 -07:00
cmn.PanicCrisis(cmn.Fmt("Error reading commit: %v", err))
}
2016-04-02 09:10:16 -07:00
return commit
}
2017-10-04 12:04:23 -07:00
// SaveBlock persists the given block, blockParts, and seenCommit to the underlying db.
2016-04-02 09:10:16 -07:00
// blockParts: Must be parts of the block
// seenCommit: The +2/3 precommits that were seen which committed at height.
// If all the nodes restart after committing a block,
// we need this to reload the precommits to catch-up nodes to the
// most recent height. Otherwise they'd stall at H-1.
func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
if block == nil {
2017-10-04 12:04:23 -07:00
cmn.PanicSanity("BlockStore can only save a non-nil block")
}
2014-09-14 15:37:32 -07:00
height := block.Height
if g, w := height, bs.Height()+1; g != w {
cmn.PanicSanity(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g))
2014-08-10 16:35:08 -07:00
}
if !blockParts.IsComplete() {
2017-10-03 15:49:20 -07:00
cmn.PanicSanity(cmn.Fmt("BlockStore can only save complete block part sets"))
}
2014-12-23 01:35:54 -08:00
// Save block meta
2017-02-14 12:33:14 -08:00
blockMeta := types.NewBlockMeta(block, blockParts)
metaBytes := wire.BinaryBytes(blockMeta)
bs.db.Set(calcBlockMetaKey(height), metaBytes)
2014-12-23 01:35:54 -08:00
// Save block parts
for i := 0; i < blockParts.Total(); i++ {
2016-12-22 18:51:58 -08:00
bs.saveBlockPart(height, i, blockParts.GetPart(i))
}
2014-12-23 01:35:54 -08:00
2016-04-02 09:10:16 -07:00
// Save block commit (duplicate and separate from the Block)
blockCommitBytes := wire.BinaryBytes(block.LastCommit)
bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes)
2016-04-02 09:10:16 -07:00
// Save seen commit (seen +2/3 precommits for block)
2016-11-19 16:32:35 -08:00
// NOTE: we can delete this at a later height
2016-04-02 09:10:16 -07:00
seenCommitBytes := wire.BinaryBytes(seenCommit)
bs.db.Set(calcSeenCommitKey(height), seenCommitBytes)
2014-12-23 01:35:54 -08:00
// Save new BlockStoreStateJSON descriptor
BlockStoreStateJSON{Height: height}.Save(bs.db)
// Done!
2016-12-19 21:45:45 -08:00
bs.mtx.Lock()
bs.height = height
2016-12-19 21:45:45 -08:00
bs.mtx.Unlock()
2016-12-06 02:52:07 -08:00
// Flush
bs.db.SetSync(nil, nil)
2014-08-10 16:35:08 -07:00
}
func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) {
2016-12-22 18:51:58 -08:00
if height != bs.Height()+1 {
2017-10-03 15:49:20 -07:00
cmn.PanicSanity(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
}
2015-07-25 15:45:45 -07:00
partBytes := wire.BinaryBytes(part)
bs.db.Set(calcBlockPartKey(height, index), partBytes)
}
//-----------------------------------------------------------------------------
func calcBlockMetaKey(height int64) []byte {
return []byte(fmt.Sprintf("H:%v", height))
2014-08-10 16:35:08 -07:00
}
func calcBlockPartKey(height int64, partIndex int) []byte {
return []byte(fmt.Sprintf("P:%v:%v", height, partIndex))
}
func calcBlockCommitKey(height int64) []byte {
2016-04-02 09:10:16 -07:00
return []byte(fmt.Sprintf("C:%v", height))
}
func calcSeenCommitKey(height int64) []byte {
2016-04-02 09:10:16 -07:00
return []byte(fmt.Sprintf("SC:%v", height))
}
//-----------------------------------------------------------------------------
var blockStoreKey = []byte("blockStore")
2014-12-23 01:35:54 -08:00
type BlockStoreStateJSON struct {
Height int64
}
2017-10-04 12:04:23 -07:00
// Save persists the blockStore state to the database as JSON.
func (bsj BlockStoreStateJSON) Save(db dbm.DB) {
bytes, err := json.Marshal(bsj)
if err != nil {
2017-10-03 15:49:20 -07:00
cmn.PanicSanity(cmn.Fmt("Could not marshal state bytes: %v", err))
}
2016-12-06 02:52:07 -08:00
db.SetSync(blockStoreKey, bytes)
}
2017-10-04 12:04:23 -07:00
// LoadBlockStoreStateJSON returns the BlockStoreStateJSON as loaded from disk.
// If no BlockStoreStateJSON was previously persisted, it returns the zero value.
func LoadBlockStoreStateJSON(db dbm.DB) BlockStoreStateJSON {
bytes := db.Get(blockStoreKey)
if bytes == nil {
2014-12-23 01:35:54 -08:00
return BlockStoreStateJSON{
Height: 0,
}
}
2014-12-23 01:35:54 -08:00
bsj := BlockStoreStateJSON{}
err := json.Unmarshal(bytes, &bsj)
if err != nil {
2017-10-03 15:49:20 -07:00
cmn.PanicCrisis(cmn.Fmt("Could not unmarshal bytes: %X", bytes))
}
return bsj
}