package blocks
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"io"
|
|
|
|
. "github.com/tendermint/tendermint/binary"
|
|
. "github.com/tendermint/tendermint/common"
|
|
db_ "github.com/tendermint/tendermint/db"
|
|
)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/*
|
|
Simple low level store for blocks, which is actually stored as separte parts (wire format).
|
|
*/
|
|
type BlockStore struct {
|
|
height uint32
|
|
db db_.DB
|
|
}
|
|
|
|
func NewBlockStore(db db_.DB) *BlockStore {
|
|
bsjson := LoadBlockStoreJSON(db)
|
|
return &BlockStore{
|
|
height: bsjson.Height,
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
// Height() returns the last known contiguous block height.
|
|
func (bs *BlockStore) Height() uint32 {
|
|
return bs.height
|
|
}
|
|
|
|
func (bs *BlockStore) GetReader(key []byte) io.Reader {
|
|
bytez := bs.db.Get(key)
|
|
if bytez == nil {
|
|
return nil
|
|
}
|
|
return bytes.NewReader(bytez)
|
|
}
|
|
|
|
func (bs *BlockStore) LoadBlock(height uint32) *Block {
|
|
var n int64
|
|
var err error
|
|
meta := ReadBlockMeta(bs.GetReader(calcBlockMetaKey(height)), &n, &err)
|
|
if err != nil {
|
|
Panicf("Error reading block meta: %v", err)
|
|
}
|
|
bytez := []byte{}
|
|
for i := uint16(0); i < meta.Parts.Total; i++ {
|
|
part := bs.LoadBlockPart(height, i)
|
|
bytez = append(bytez, part.Bytes...)
|
|
}
|
|
block := ReadBlock(bytes.NewReader(bytez), &n, &err)
|
|
if err != nil {
|
|
Panicf("Error reading block: %v", err)
|
|
}
|
|
return block
|
|
}
|
|
|
|
func (bs *BlockStore) LoadBlockPart(height uint32, index uint16) *Part {
|
|
var n int64
|
|
var err error
|
|
part := ReadPart(bs.GetReader(calcBlockPartKey(height, index)), &n, &err)
|
|
if err != nil {
|
|
Panicf("Error reading block part: %v", err)
|
|
}
|
|
return part
|
|
}
|
|
|
|
func (bs *BlockStore) LoadBlockMeta(height uint32) *BlockMeta {
|
|
var n int64
|
|
var err error
|
|
meta := ReadBlockMeta(bs.GetReader(calcBlockMetaKey(height)), &n, &err)
|
|
if err != nil {
|
|
Panicf("Error reading block meta: %v", err)
|
|
}
|
|
return meta
|
|
}
|
|
|
|
func (bs *BlockStore) LoadBlockValidation(height uint32) *Validation {
|
|
var n int64
|
|
var err error
|
|
validation := ReadValidation(bs.GetReader(calcBlockValidationKey(height)), &n, &err)
|
|
if err != nil {
|
|
Panicf("Error reading validation: %v", err)
|
|
}
|
|
return validation
|
|
}
|
|
|
|
func (bs *BlockStore) SaveBlock(block *Block, blockParts *PartSet) {
|
|
height := block.Height
|
|
if height != bs.height+1 {
|
|
Panicf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height)
|
|
}
|
|
if !blockParts.IsComplete() {
|
|
Panicf("BlockStore can only save complete block part sets")
|
|
}
|
|
meta := &BlockMeta{
|
|
Hash: block.Hash(),
|
|
Parts: blockParts.Header(),
|
|
Header: block.Header,
|
|
}
|
|
// Save block meta
|
|
metaBytes := BinaryBytes(meta)
|
|
bs.db.Set(calcBlockMetaKey(height), metaBytes)
|
|
// Save block parts
|
|
for i := uint16(0); i < blockParts.Total(); i++ {
|
|
bs.saveBlockPart(height, i, blockParts.GetPart(i))
|
|
}
|
|
// Save block validation (duplicate and separate)
|
|
validationBytes := BinaryBytes(block.Validation)
|
|
bs.db.Set(calcBlockValidationKey(height), validationBytes)
|
|
// Save new BlockStoreJSON descriptor
|
|
BlockStoreJSON{Height: height}.Save(bs.db)
|
|
// Done!
|
|
bs.height = height
|
|
}
|
|
|
|
func (bs *BlockStore) saveBlockPart(height uint32, index uint16, part *Part) {
|
|
if height != bs.height+1 {
|
|
Panicf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height)
|
|
}
|
|
partBytes := BinaryBytes(part)
|
|
bs.db.Set(calcBlockPartKey(height, index), partBytes)
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
type BlockMeta struct {
|
|
Hash []byte // The BlockHash
|
|
Parts PartSetHeader // The PartSetHeader, for transfer
|
|
Header *Header // The block's Header
|
|
}
|
|
|
|
func ReadBlockMeta(r io.Reader, n *int64, err *error) *BlockMeta {
|
|
return &BlockMeta{
|
|
Hash: ReadByteSlice(r, n, err),
|
|
Parts: ReadPartSetHeader(r, n, err),
|
|
Header: ReadHeader(r, n, err),
|
|
}
|
|
}
|
|
|
|
func (bm *BlockMeta) WriteTo(w io.Writer) (n int64, err error) {
|
|
WriteByteSlice(w, bm.Hash, &n, &err)
|
|
WriteBinary(w, bm.Parts, &n, &err)
|
|
WriteBinary(w, bm.Header, &n, &err)
|
|
return
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
func calcBlockMetaKey(height uint32) []byte {
|
|
buf := [5]byte{'H'}
|
|
binary.BigEndian.PutUint32(buf[1:5], height)
|
|
return buf[:]
|
|
}
|
|
|
|
func calcBlockPartKey(height uint32, partIndex uint16) []byte {
|
|
buf := [7]byte{'P'}
|
|
binary.BigEndian.PutUint32(buf[1:5], height)
|
|
binary.BigEndian.PutUint16(buf[5:7], partIndex)
|
|
return buf[:]
|
|
}
|
|
|
|
func calcBlockValidationKey(height uint32) []byte {
|
|
buf := [5]byte{'V'}
|
|
binary.BigEndian.PutUint32(buf[1:5], height)
|
|
return buf[:]
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
var blockStoreKey = []byte("blockStore")
|
|
|
|
type BlockStoreJSON struct {
|
|
Height uint32
|
|
}
|
|
|
|
func (bsj BlockStoreJSON) Save(db db_.DB) {
|
|
bytes, err := json.Marshal(bsj)
|
|
if err != nil {
|
|
Panicf("Could not marshal state bytes: %v", err)
|
|
}
|
|
db.Set(blockStoreKey, bytes)
|
|
}
|
|
|
|
func LoadBlockStoreJSON(db db_.DB) BlockStoreJSON {
|
|
bytes := db.Get(blockStoreKey)
|
|
if bytes == nil {
|
|
return BlockStoreJSON{
|
|
Height: 0,
|
|
}
|
|
}
|
|
bsj := BlockStoreJSON{}
|
|
err := json.Unmarshal(bytes, &bsj)
|
|
if err != nil {
|
|
Panicf("Could not unmarshal bytes: %X", bytes)
|
|
}
|
|
return bsj
|
|
}
|