mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-08 06:15:33 +00:00
store: order-preserving varint key encoding (#5771)
This commit is contained in:
@@ -38,6 +38,9 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
||||
|
||||
- Blockchain Protocol
|
||||
|
||||
- Data Storage
|
||||
- [store/state/evidence/light] \#5771 Use an order-preserving varint key encoding (@cmwaters)
|
||||
|
||||
### FEATURES
|
||||
|
||||
### IMPROVEMENTS
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
"github.com/google/orderedcode"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
clist "github.com/tendermint/tendermint/libs/clist"
|
||||
@@ -21,8 +22,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
baseKeyCommitted = byte(0x00)
|
||||
baseKeyPending = byte(0x01)
|
||||
// prefixes are unique across all tm db's
|
||||
prefixCommitted = int64(8)
|
||||
prefixPending = int64(9)
|
||||
)
|
||||
|
||||
// Pool maintains a pool of valid evidence to be broadcasted and committed
|
||||
@@ -67,7 +69,7 @@ func NewPool(evidenceDB dbm.DB, stateDB sm.Store, blockStore BlockStore) (*Pool,
|
||||
// if pending evidence already in db, in event of prior failure, then check for expiration,
|
||||
// update the size and load it back to the evidenceList
|
||||
pool.pruningHeight, pool.pruningTime = pool.removeExpiredPendingEvidence()
|
||||
evList, _, err := pool.listEvidence(baseKeyPending, -1)
|
||||
evList, _, err := pool.listEvidence(prefixPending, -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -84,7 +86,7 @@ func (evpool *Pool) PendingEvidence(maxBytes int64) ([]types.Evidence, int64) {
|
||||
if evpool.Size() == 0 {
|
||||
return []types.Evidence{}, 0
|
||||
}
|
||||
evidence, size, err := evpool.listEvidence(baseKeyPending, maxBytes)
|
||||
evidence, size, err := evpool.listEvidence(prefixPending, maxBytes)
|
||||
if err != nil {
|
||||
evpool.logger.Error("Unable to retrieve pending evidence", "err", err)
|
||||
}
|
||||
@@ -402,7 +404,7 @@ func (evpool *Pool) markEvidenceAsCommitted(evidence types.EvidenceList) {
|
||||
|
||||
// listEvidence retrieves lists evidence from oldest to newest within maxBytes.
|
||||
// If maxBytes is -1, there's no cap on the size of returned evidence.
|
||||
func (evpool *Pool) listEvidence(prefixKey byte, maxBytes int64) ([]types.Evidence, int64, error) {
|
||||
func (evpool *Pool) listEvidence(prefixKey int64, maxBytes int64) ([]types.Evidence, int64, error) {
|
||||
var (
|
||||
evSize int64
|
||||
totalSize int64
|
||||
@@ -410,11 +412,12 @@ func (evpool *Pool) listEvidence(prefixKey byte, maxBytes int64) ([]types.Eviden
|
||||
evList tmproto.EvidenceList // used for calculating the bytes size
|
||||
)
|
||||
|
||||
iter, err := dbm.IteratePrefix(evpool.evidenceStore, []byte{prefixKey})
|
||||
iter, err := dbm.IteratePrefix(evpool.evidenceStore, prefixToBytes(prefixKey))
|
||||
if err != nil {
|
||||
return nil, totalSize, fmt.Errorf("database error: %v", err)
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
for ; iter.Valid(); iter.Next() {
|
||||
var evpb tmproto.Evidence
|
||||
err := evpb.Unmarshal(iter.Value())
|
||||
@@ -446,7 +449,7 @@ func (evpool *Pool) listEvidence(prefixKey byte, maxBytes int64) ([]types.Eviden
|
||||
}
|
||||
|
||||
func (evpool *Pool) removeExpiredPendingEvidence() (int64, time.Time) {
|
||||
iter, err := dbm.IteratePrefix(evpool.evidenceStore, []byte{baseKeyPending})
|
||||
iter, err := dbm.IteratePrefix(evpool.evidenceStore, prefixToBytes(prefixPending))
|
||||
if err != nil {
|
||||
evpool.logger.Error("Unable to iterate over pending evidence", "err", err)
|
||||
return evpool.State().LastBlockHeight, evpool.State().LastBlockTime
|
||||
@@ -511,19 +514,28 @@ func evMapKey(ev types.Evidence) string {
|
||||
return string(ev.Hash())
|
||||
}
|
||||
|
||||
// big endian padded hex
|
||||
func bE(h int64) string {
|
||||
return fmt.Sprintf("%0.16X", h)
|
||||
func prefixToBytes(prefix int64) []byte {
|
||||
key, err := orderedcode.Append(nil, prefix)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func keyCommitted(evidence types.Evidence) []byte {
|
||||
return append([]byte{baseKeyCommitted}, keySuffix(evidence)...)
|
||||
var height int64 = evidence.Height()
|
||||
key, err := orderedcode.Append(nil, prefixCommitted, height, string(evidence.Hash()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func keyPending(evidence types.Evidence) []byte {
|
||||
return append([]byte{baseKeyPending}, keySuffix(evidence)...)
|
||||
}
|
||||
|
||||
func keySuffix(evidence types.Evidence) []byte {
|
||||
return []byte(fmt.Sprintf("%s/%X", bE(evidence.Height()), evidence.Hash()))
|
||||
var height int64 = evidence.Height()
|
||||
key, err := orderedcode.Append(nil, prefixPending, height, string(evidence.Hash()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
1
go.mod
1
go.mod
@@ -15,6 +15,7 @@ require (
|
||||
github.com/go-logfmt/logfmt v0.5.0
|
||||
github.com/gogo/protobuf v1.3.1
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/google/orderedcode v0.0.1
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/gtank/merlin v0.1.1
|
||||
github.com/hdevalence/ed25519consensus v0.0.0-20201207055737-7fde80a9d5ff
|
||||
|
||||
2
go.sum
2
go.sum
@@ -221,6 +221,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
|
||||
github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
|
||||
@@ -3,9 +3,8 @@ package db
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/orderedcode"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
tmsync "github.com/tendermint/tendermint/libs/sync"
|
||||
@@ -14,8 +13,9 @@ import (
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var (
|
||||
sizeKey = []byte("size")
|
||||
const (
|
||||
prefixLightBlock = int64(0x0a)
|
||||
prefixSize = int64(0x0b)
|
||||
)
|
||||
|
||||
type dbs struct {
|
||||
@@ -30,13 +30,17 @@ type dbs struct {
|
||||
// want to use one DB with many light clients).
|
||||
func New(db dbm.DB, prefix string) store.Store {
|
||||
|
||||
lightStore := &dbs{db: db, prefix: prefix}
|
||||
|
||||
// retrieve the size of the db
|
||||
size := uint16(0)
|
||||
bz, err := db.Get(sizeKey)
|
||||
bz, err := lightStore.db.Get(lightStore.sizeKey())
|
||||
if err == nil && len(bz) > 0 {
|
||||
size = unmarshalSize(bz)
|
||||
}
|
||||
lightStore.size = size
|
||||
|
||||
return &dbs{db: db, prefix: prefix, size: size}
|
||||
return lightStore
|
||||
}
|
||||
|
||||
// SaveLightBlock persists LightBlock to the db.
|
||||
@@ -65,7 +69,7 @@ func (s *dbs) SaveLightBlock(lb *types.LightBlock) error {
|
||||
if err = b.Set(s.lbKey(lb.Height), lbBz); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = b.Set(sizeKey, marshalSize(s.size+1)); err != nil {
|
||||
if err = b.Set(s.sizeKey(), marshalSize(s.size+1)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = b.WriteSync(); err != nil {
|
||||
@@ -93,7 +97,7 @@ func (s *dbs) DeleteLightBlock(height int64) error {
|
||||
if err := b.Delete(s.lbKey(height)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.Set(sizeKey, marshalSize(s.size-1)); err != nil {
|
||||
if err := b.Set(s.sizeKey(), marshalSize(s.size-1)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.WriteSync(); err != nil {
|
||||
@@ -147,13 +151,8 @@ func (s *dbs) LastLightBlockHeight() (int64, error) {
|
||||
}
|
||||
defer itr.Close()
|
||||
|
||||
for itr.Valid() {
|
||||
key := itr.Key()
|
||||
_, height, ok := parseLbKey(key)
|
||||
if ok {
|
||||
return height, nil
|
||||
}
|
||||
itr.Next()
|
||||
if itr.Valid() {
|
||||
return s.decodeLbKey(itr.Key())
|
||||
}
|
||||
|
||||
return -1, itr.Error()
|
||||
@@ -172,13 +171,8 @@ func (s *dbs) FirstLightBlockHeight() (int64, error) {
|
||||
}
|
||||
defer itr.Close()
|
||||
|
||||
for itr.Valid() {
|
||||
key := itr.Key()
|
||||
_, height, ok := parseLbKey(key)
|
||||
if ok {
|
||||
return height, nil
|
||||
}
|
||||
itr.Next()
|
||||
if itr.Valid() {
|
||||
return s.decodeLbKey(itr.Key())
|
||||
}
|
||||
|
||||
return -1, itr.Error()
|
||||
@@ -202,13 +196,12 @@ func (s *dbs) LightBlockBefore(height int64) (*types.LightBlock, error) {
|
||||
}
|
||||
defer itr.Close()
|
||||
|
||||
for itr.Valid() {
|
||||
key := itr.Key()
|
||||
_, existingHeight, ok := parseLbKey(key)
|
||||
if ok {
|
||||
return s.LightBlock(existingHeight)
|
||||
if itr.Valid() {
|
||||
existingHeight, err := s.decodeLbKey(itr.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
itr.Next()
|
||||
return s.LightBlock(existingHeight)
|
||||
}
|
||||
if err = itr.Error(); err != nil {
|
||||
return nil, err
|
||||
@@ -248,11 +241,12 @@ func (s *dbs) Prune(size uint16) error {
|
||||
pruned := 0
|
||||
for itr.Valid() && numToPrune > 0 {
|
||||
key := itr.Key()
|
||||
_, height, ok := parseLbKey(key)
|
||||
if ok {
|
||||
if err = b.Delete(s.lbKey(height)); err != nil {
|
||||
return err
|
||||
}
|
||||
height, err := s.decodeLbKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = b.Delete(s.lbKey(height)); err != nil {
|
||||
return err
|
||||
}
|
||||
itr.Next()
|
||||
numToPrune--
|
||||
@@ -273,7 +267,7 @@ func (s *dbs) Prune(size uint16) error {
|
||||
|
||||
s.size -= uint16(pruned)
|
||||
|
||||
if wErr := s.db.SetSync(sizeKey, marshalSize(s.size)); wErr != nil {
|
||||
if wErr := s.db.SetSync(s.sizeKey(), marshalSize(size)); wErr != nil {
|
||||
return fmt.Errorf("failed to persist size: %w", wErr)
|
||||
}
|
||||
|
||||
@@ -289,32 +283,39 @@ func (s *dbs) Size() uint16 {
|
||||
return s.size
|
||||
}
|
||||
|
||||
func (s *dbs) lbKey(height int64) []byte {
|
||||
return []byte(fmt.Sprintf("lb/%s/%020d", s.prefix, height))
|
||||
}
|
||||
|
||||
var keyPattern = regexp.MustCompile(`^(lb)/([^/]*)/([0-9]+)$`)
|
||||
|
||||
func parseKey(key []byte) (part string, prefix string, height int64, ok bool) {
|
||||
submatch := keyPattern.FindSubmatch(key)
|
||||
if submatch == nil {
|
||||
return "", "", 0, false
|
||||
}
|
||||
part = string(submatch[1])
|
||||
prefix = string(submatch[2])
|
||||
height, err := strconv.ParseInt(string(submatch[3]), 10, 64)
|
||||
func (s *dbs) sizeKey() []byte {
|
||||
key, err := orderedcode.Append(nil, s.prefix, prefixSize)
|
||||
if err != nil {
|
||||
return "", "", 0, false
|
||||
panic(err)
|
||||
}
|
||||
ok = true // good!
|
||||
return
|
||||
return key
|
||||
}
|
||||
|
||||
func parseLbKey(key []byte) (prefix string, height int64, ok bool) {
|
||||
var part string
|
||||
part, prefix, height, ok = parseKey(key)
|
||||
if part != "lb" {
|
||||
return "", 0, false
|
||||
func (s *dbs) lbKey(height int64) []byte {
|
||||
key, err := orderedcode.Append(nil, s.prefix, prefixLightBlock, height)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func (s *dbs) decodeLbKey(key []byte) (height int64, err error) {
|
||||
var (
|
||||
dbPrefix string
|
||||
lightBlockPrefix int64
|
||||
)
|
||||
remaining, err := orderedcode.Parse(string(key), &dbPrefix, &lightBlockPrefix, &height)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to parse light block key: %w", err)
|
||||
}
|
||||
if len(remaining) != 0 {
|
||||
err = fmt.Errorf("expected no remainder when parsing light block key but got: %s", remaining)
|
||||
}
|
||||
if lightBlockPrefix != prefixLightBlock {
|
||||
err = fmt.Errorf("expected light block prefix but got: %d", lightBlockPrefix)
|
||||
}
|
||||
if dbPrefix != s.prefix {
|
||||
err = fmt.Errorf("parsed key has a different prefix. Expected: %s, got: %s", s.prefix, dbPrefix)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ func Test_LightBlockBefore(t *testing.T) {
|
||||
if assert.NotNil(t, h) {
|
||||
assert.EqualValues(t, 2, h.Height)
|
||||
}
|
||||
|
||||
_, err = dbStore.LightBlockBefore(2)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func Test_Prune(t *testing.T) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/google/orderedcode"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@@ -25,16 +26,31 @@ const (
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
func calcValidatorsKey(height int64) []byte {
|
||||
return []byte(fmt.Sprintf("validatorsKey:%v", height))
|
||||
const (
|
||||
// prefixes are unique across all tm db's
|
||||
prefixValidators = int64(5)
|
||||
prefixConsensusParams = int64(6)
|
||||
prefixABCIResponses = int64(7)
|
||||
)
|
||||
|
||||
func encodeKey(prefix int64, height int64) []byte {
|
||||
res, err := orderedcode.Append(nil, prefix, height)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func calcConsensusParamsKey(height int64) []byte {
|
||||
return []byte(fmt.Sprintf("consensusParamsKey:%v", height))
|
||||
func validatorsKey(height int64) []byte {
|
||||
return encodeKey(prefixValidators, height)
|
||||
}
|
||||
|
||||
func calcABCIResponsesKey(height int64) []byte {
|
||||
return []byte(fmt.Sprintf("abciResponsesKey:%v", height))
|
||||
func consensusParamsKey(height int64) []byte {
|
||||
return encodeKey(prefixConsensusParams, height)
|
||||
}
|
||||
|
||||
func abciResponsesKey(height int64) []byte {
|
||||
return encodeKey(prefixABCIResponses, height)
|
||||
}
|
||||
|
||||
//----------------------
|
||||
@@ -276,13 +292,13 @@ func (store dbStore) PruneStates(from int64, to int64) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = batch.Set(calcValidatorsKey(h), bz)
|
||||
err = batch.Set(validatorsKey(h), bz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = batch.Delete(calcValidatorsKey(h))
|
||||
err = batch.Delete(validatorsKey(h))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -306,19 +322,19 @@ func (store dbStore) PruneStates(from int64, to int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = batch.Set(calcConsensusParamsKey(h), bz)
|
||||
err = batch.Set(consensusParamsKey(h), bz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = batch.Delete(calcConsensusParamsKey(h))
|
||||
err = batch.Delete(consensusParamsKey(h))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = batch.Delete(calcABCIResponsesKey(h))
|
||||
err = batch.Delete(abciResponsesKey(h))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -361,7 +377,7 @@ func ABCIResponsesResultsHash(ar *tmstate.ABCIResponses) []byte {
|
||||
// before we called s.Save(). It can also be used to produce Merkle proofs of
|
||||
// the result of txs.
|
||||
func (store dbStore) LoadABCIResponses(height int64) (*tmstate.ABCIResponses, error) {
|
||||
buf, err := store.db.Get(calcABCIResponsesKey(height))
|
||||
buf, err := store.db.Get(abciResponsesKey(height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -403,7 +419,7 @@ func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCI
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.db.SetSync(calcABCIResponsesKey(height), bz)
|
||||
err = store.db.SetSync(abciResponsesKey(height), bz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -416,6 +432,7 @@ func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCI
|
||||
// LoadValidators loads the ValidatorSet for a given height.
|
||||
// Returns ErrNoValSetForHeight if the validator set can't be found for this height.
|
||||
func (store dbStore) LoadValidators(height int64) (*types.ValidatorSet, error) {
|
||||
|
||||
valInfo, err := loadValidatorsInfo(store.db, height)
|
||||
if err != nil {
|
||||
return nil, ErrNoValSetForHeight{height}
|
||||
@@ -462,7 +479,7 @@ func lastStoredHeightFor(height, lastHeightChanged int64) int64 {
|
||||
|
||||
// CONTRACT: Returned ValidatorsInfo can be mutated.
|
||||
func loadValidatorsInfo(db dbm.DB, height int64) (*tmstate.ValidatorsInfo, error) {
|
||||
buf, err := db.Get(calcValidatorsKey(height))
|
||||
buf, err := db.Get(validatorsKey(height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -510,7 +527,7 @@ func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, valSet
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.db.Set(calcValidatorsKey(height), bz)
|
||||
err = store.db.Set(validatorsKey(height), bz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -549,7 +566,7 @@ func (store dbStore) LoadConsensusParams(height int64) (tmproto.ConsensusParams,
|
||||
}
|
||||
|
||||
func (store dbStore) loadConsensusParamsInfo(height int64) (*tmstate.ConsensusParamsInfo, error) {
|
||||
buf, err := store.db.Get(calcConsensusParamsKey(height))
|
||||
buf, err := store.db.Get(consensusParamsKey(height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -585,7 +602,7 @@ func (store dbStore) saveConsensusParamsInfo(nextHeight, changeHeight int64, par
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.db.Set(calcConsensusParamsKey(nextHeight), bz)
|
||||
err = store.db.Set(consensusParamsKey(nextHeight), bz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/google/orderedcode"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
tmsync "github.com/tendermint/tendermint/libs/sync"
|
||||
@@ -126,7 +127,7 @@ func (bs *BlockStore) LoadBlock(height int64) *types.Block {
|
||||
// If no block is found for that hash, it returns nil.
|
||||
// Panics if it fails to parse height associated with the given hash.
|
||||
func (bs *BlockStore) LoadBlockByHash(hash []byte) *types.Block {
|
||||
bz, err := bs.db.Get(calcBlockHashKey(hash))
|
||||
bz, err := bs.db.Get(blockHashKey(hash))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -149,7 +150,7 @@ func (bs *BlockStore) LoadBlockByHash(hash []byte) *types.Block {
|
||||
func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
|
||||
var pbpart = new(tmproto.Part)
|
||||
|
||||
bz, err := bs.db.Get(calcBlockPartKey(height, index))
|
||||
bz, err := bs.db.Get(blockPartKey(height, index))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -173,7 +174,7 @@ func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
|
||||
// If no block is found for the given height, it returns nil.
|
||||
func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
||||
var pbbm = new(tmproto.BlockMeta)
|
||||
bz, err := bs.db.Get(calcBlockMetaKey(height))
|
||||
bz, err := bs.db.Get(blockMetaKey(height))
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -202,7 +203,7 @@ func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
||||
// If no commit is found for the given height, it returns nil.
|
||||
func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
|
||||
var pbc = new(tmproto.Commit)
|
||||
bz, err := bs.db.Get(calcBlockCommitKey(height))
|
||||
bz, err := bs.db.Get(blockCommitKey(height))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -225,7 +226,7 @@ func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
|
||||
// a new block at `height + 1` that includes this commit in its block.LastCommit.
|
||||
func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit {
|
||||
var pbc = new(tmproto.Commit)
|
||||
bz, err := bs.db.Get(calcSeenCommitKey(height))
|
||||
bz, err := bs.db.Get(seenCommitKey(height))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -285,20 +286,20 @@ func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) {
|
||||
if meta == nil { // assume already deleted
|
||||
continue
|
||||
}
|
||||
if err := batch.Delete(calcBlockMetaKey(h)); err != nil {
|
||||
if err := batch.Delete(blockMetaKey(h)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := batch.Delete(calcBlockHashKey(meta.BlockID.Hash)); err != nil {
|
||||
if err := batch.Delete(blockHashKey(meta.BlockID.Hash)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := batch.Delete(calcBlockCommitKey(h)); err != nil {
|
||||
if err := batch.Delete(blockCommitKey(h)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := batch.Delete(calcSeenCommitKey(h)); err != nil {
|
||||
if err := batch.Delete(seenCommitKey(h)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for p := 0; p < int(meta.BlockID.PartSetHeader.Total); p++ {
|
||||
if err := batch.Delete(calcBlockPartKey(h, p)); err != nil {
|
||||
if err := batch.Delete(blockPartKey(h, p)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@@ -359,17 +360,17 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
|
||||
panic("nil blockmeta")
|
||||
}
|
||||
metaBytes := mustEncode(pbm)
|
||||
if err := bs.db.Set(calcBlockMetaKey(height), metaBytes); err != nil {
|
||||
if err := bs.db.Set(blockMetaKey(height), metaBytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := bs.db.Set(calcBlockHashKey(hash), []byte(fmt.Sprintf("%d", height))); err != nil {
|
||||
if err := bs.db.Set(blockHashKey(hash), []byte(fmt.Sprintf("%d", height))); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Save block commit (duplicate and separate from the Block)
|
||||
pbc := block.LastCommit.ToProto()
|
||||
blockCommitBytes := mustEncode(pbc)
|
||||
if err := bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes); err != nil {
|
||||
if err := bs.db.Set(blockCommitKey(height-1), blockCommitBytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -377,7 +378,7 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
|
||||
// NOTE: we can delete this at a later height
|
||||
pbsc := seenCommit.ToProto()
|
||||
seenCommitBytes := mustEncode(pbsc)
|
||||
if err := bs.db.Set(calcSeenCommitKey(height), seenCommitBytes); err != nil {
|
||||
if err := bs.db.Set(seenCommitKey(height), seenCommitBytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -399,7 +400,7 @@ func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) {
|
||||
panic(fmt.Errorf("unable to make part into proto: %w", err))
|
||||
}
|
||||
partBytes := mustEncode(pbp)
|
||||
if err := bs.db.Set(calcBlockPartKey(height, index), partBytes); err != nil {
|
||||
if err := bs.db.Set(blockPartKey(height, index), partBytes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -421,29 +422,59 @@ func (bs *BlockStore) SaveSeenCommit(height int64, seenCommit *types.Commit) err
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to marshal commit: %w", err)
|
||||
}
|
||||
return bs.db.Set(calcSeenCommitKey(height), seenCommitBytes)
|
||||
return bs.db.Set(seenCommitKey(height), seenCommitBytes)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//---------------------------------- KEY ENCODING -----------------------------------------
|
||||
|
||||
func calcBlockMetaKey(height int64) []byte {
|
||||
return []byte(fmt.Sprintf("H:%v", height))
|
||||
// key prefixes
|
||||
const (
|
||||
// prefixes are unique across all tm db's
|
||||
prefixBlockMeta = int64(0)
|
||||
prefixBlockPart = int64(1)
|
||||
prefixBlockCommit = int64(2)
|
||||
prefixSeenCommit = int64(3)
|
||||
prefixBlockHash = int64(4)
|
||||
)
|
||||
|
||||
func blockMetaKey(height int64) []byte {
|
||||
key, err := orderedcode.Append(nil, prefixBlockMeta, height)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func calcBlockPartKey(height int64, partIndex int) []byte {
|
||||
return []byte(fmt.Sprintf("P:%v:%v", height, partIndex))
|
||||
func blockPartKey(height int64, partIndex int) []byte {
|
||||
key, err := orderedcode.Append(nil, prefixBlockPart, height, int64(partIndex))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func calcBlockCommitKey(height int64) []byte {
|
||||
return []byte(fmt.Sprintf("C:%v", height))
|
||||
func blockCommitKey(height int64) []byte {
|
||||
key, err := orderedcode.Append(nil, prefixBlockCommit, height)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func calcSeenCommitKey(height int64) []byte {
|
||||
return []byte(fmt.Sprintf("SC:%v", height))
|
||||
func seenCommitKey(height int64) []byte {
|
||||
key, err := orderedcode.Append(nil, prefixSeenCommit, height)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func calcBlockHashKey(hash []byte) []byte {
|
||||
return []byte(fmt.Sprintf("BH:%x", hash))
|
||||
func blockHashKey(hash []byte) []byte {
|
||||
key, err := orderedcode.Append(nil, prefixBlockHash, string(hash))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -156,7 +156,6 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
// TODO: This test should be simplified ...
|
||||
|
||||
func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer)))
|
||||
defer cleanup()
|
||||
@@ -193,7 +192,6 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
// End of setup, test data
|
||||
|
||||
commitAtH10 := makeTestCommit(10, tmtime.Now())
|
||||
tuples := []struct {
|
||||
block *types.Block
|
||||
@@ -302,29 +300,29 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
if tuple.corruptBlockInDB {
|
||||
err := db.Set(calcBlockMetaKey(tuple.block.Height), []byte("block-bogus"))
|
||||
err := db.Set(blockMetaKey(tuple.block.Height), []byte("block-bogus"))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
bBlock := bs.LoadBlock(tuple.block.Height)
|
||||
bBlockMeta := bs.LoadBlockMeta(tuple.block.Height)
|
||||
|
||||
if tuple.eraseSeenCommitInDB {
|
||||
err := db.Delete(calcSeenCommitKey(tuple.block.Height))
|
||||
err := db.Delete(seenCommitKey(tuple.block.Height))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if tuple.corruptSeenCommitInDB {
|
||||
err := db.Set(calcSeenCommitKey(tuple.block.Height), []byte("bogus-seen-commit"))
|
||||
err := db.Set(seenCommitKey(tuple.block.Height), []byte("bogus-seen-commit"))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
bSeenCommit := bs.LoadSeenCommit(tuple.block.Height)
|
||||
|
||||
commitHeight := tuple.block.Height - 1
|
||||
if tuple.eraseCommitInDB {
|
||||
err := db.Delete(calcBlockCommitKey(commitHeight))
|
||||
err := db.Delete(blockCommitKey(commitHeight))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if tuple.corruptCommitInDB {
|
||||
err := db.Set(calcBlockCommitKey(commitHeight), []byte("foo-bogus"))
|
||||
err := db.Set(blockCommitKey(commitHeight), []byte("foo-bogus"))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
bCommit := bs.LoadBlockCommit(commitHeight)
|
||||
@@ -404,7 +402,7 @@ func TestLoadBlockPart(t *testing.T) {
|
||||
require.Nil(t, res, "a non-existent block part should return nil")
|
||||
|
||||
// 2. Next save a corrupted block then try to load it
|
||||
err := db.Set(calcBlockPartKey(height, index), []byte("Tendermint"))
|
||||
err := db.Set(blockPartKey(height, index), []byte("Tendermint"))
|
||||
require.NoError(t, err)
|
||||
res, _, panicErr = doFn(loadPart)
|
||||
require.NotNil(t, panicErr, "expecting a non-nil panic")
|
||||
@@ -413,7 +411,7 @@ func TestLoadBlockPart(t *testing.T) {
|
||||
// 3. A good block serialized and saved to the DB should be retrievable
|
||||
pb1, err := part1.ToProto()
|
||||
require.NoError(t, err)
|
||||
err = db.Set(calcBlockPartKey(height, index), mustEncode(pb1))
|
||||
err = db.Set(blockPartKey(height, index), mustEncode(pb1))
|
||||
require.NoError(t, err)
|
||||
gotPart, _, panicErr := doFn(loadPart)
|
||||
require.Nil(t, panicErr, "an existent and proper block should not panic")
|
||||
@@ -524,7 +522,7 @@ func TestLoadBlockMeta(t *testing.T) {
|
||||
require.Nil(t, res, "a non-existent blockMeta should return nil")
|
||||
|
||||
// 2. Next save a corrupted blockMeta then try to load it
|
||||
err := db.Set(calcBlockMetaKey(height), []byte("Tendermint-Meta"))
|
||||
err := db.Set(blockMetaKey(height), []byte("Tendermint-Meta"))
|
||||
require.NoError(t, err)
|
||||
res, _, panicErr = doFn(loadMeta)
|
||||
require.NotNil(t, panicErr, "expecting a non-nil panic")
|
||||
@@ -535,7 +533,7 @@ func TestLoadBlockMeta(t *testing.T) {
|
||||
Version: tmversion.Consensus{
|
||||
Block: version.BlockProtocol, App: 0}, Height: 1, ProposerAddress: tmrand.Bytes(crypto.AddressSize)}}
|
||||
pbm := meta.ToProto()
|
||||
err = db.Set(calcBlockMetaKey(height), mustEncode(pbm))
|
||||
err = db.Set(blockMetaKey(height), mustEncode(pbm))
|
||||
require.NoError(t, err)
|
||||
gotMeta, _, panicErr := doFn(loadMeta)
|
||||
require.Nil(t, panicErr, "an existent and proper block should not panic")
|
||||
|
||||
Reference in New Issue
Block a user