tendermint/evidence/store.go

191 lines
5.3 KiB
Go
Raw Normal View History

2017-11-19 20:22:25 -08:00
package evidence
2017-11-02 17:26:07 -07:00
import (
"fmt"
"github.com/tendermint/tendermint/types"
dbm "github.com/tendermint/tmlibs/db"
)
/*
2017-11-19 14:06:01 -08:00
Requirements:
- Valid new evidence must be persisted immediately and never forgotten
- Uncommitted evidence must be continuously broadcast
- Uncommitted evidence has a partial order, the evidence's priority
Impl:
- First commit atomically in outqueue, pending, lookup.
- Once broadcast, remove from outqueue. No need to sync
- Once committed, atomically remove from pending and update lookup.
Schema for indexing evidence (note you need both height and hash to find a piece of evidence):
2017-11-19 19:55:59 -08:00
"evidence-lookup"/<evidence-height>/<evidence-hash> -> EvidenceInfo
"evidence-outqueue"/<priority>/<evidence-height>/<evidence-hash> -> EvidenceInfo
"evidence-pending"/<evidence-height>/<evidence-hash> -> EvidenceInfo
2017-11-02 17:26:07 -07:00
*/
2017-11-19 19:55:59 -08:00
type EvidenceInfo struct {
2017-11-02 17:26:07 -07:00
Committed bool
2017-12-26 17:34:57 -08:00
Priority int64
2017-11-02 17:26:07 -07:00
Evidence types.Evidence
}
const (
2017-11-18 16:57:55 -08:00
baseKeyLookup = "evidence-lookup" // all evidence
baseKeyOutqueue = "evidence-outqueue" // not-yet broadcast
baseKeyPending = "evidence-pending" // broadcast but not committed
)
2017-11-02 17:26:07 -07:00
func keyLookup(evidence types.Evidence) []byte {
2017-11-19 15:43:36 -08:00
return keyLookupFromHeightAndHash(evidence.Height(), evidence.Hash())
}
2017-11-19 19:55:59 -08:00
// big endian padded hex
2017-12-26 22:27:03 -08:00
func bE(h int64) string {
2017-11-19 19:55:59 -08:00
return fmt.Sprintf("%0.16X", h)
}
2017-12-26 17:34:57 -08:00
func keyLookupFromHeightAndHash(height int64, hash []byte) []byte {
2017-12-26 22:27:03 -08:00
return _key("%s/%s/%X", baseKeyLookup, bE(height), hash)
2017-11-02 17:26:07 -07:00
}
2017-12-26 17:34:57 -08:00
func keyOutqueue(evidence types.Evidence, priority int64) []byte {
2017-12-26 22:27:03 -08:00
return _key("%s/%s/%s/%X", baseKeyOutqueue, bE(priority), bE(evidence.Height()), evidence.Hash())
2017-11-02 17:26:07 -07:00
}
func keyPending(evidence types.Evidence) []byte {
2017-12-26 22:27:03 -08:00
return _key("%s/%s/%X", baseKeyPending, bE(evidence.Height()), evidence.Hash())
2017-11-18 16:57:55 -08:00
}
2017-11-19 14:06:01 -08:00
func _key(fmt_ string, o ...interface{}) []byte {
return []byte(fmt.Sprintf(fmt_, o...))
2017-11-02 17:26:07 -07:00
}
2017-11-19 15:43:36 -08:00
// EvidenceStore is a store of all the evidence we've seen, including
// evidence that has been committed, evidence that has been verified but not broadcast,
2017-11-02 17:26:07 -07:00
// and evidence that has been broadcast but not yet committed.
type EvidenceStore struct {
db dbm.DB
2017-11-02 17:26:07 -07:00
}
func NewEvidenceStore(db dbm.DB) *EvidenceStore {
2017-11-02 17:26:07 -07:00
return &EvidenceStore{
db: db,
}
}
// PriorityEvidence returns the evidence from the outqueue, sorted by highest priority.
func (store *EvidenceStore) PriorityEvidence() (evidence []types.Evidence) {
2017-11-19 19:55:59 -08:00
// reverse the order so highest priority is first
l := store.ListEvidence(baseKeyOutqueue)
l2 := make([]types.Evidence, len(l))
2017-11-20 10:59:10 -08:00
for i := range l {
2017-11-19 19:55:59 -08:00
l2[i] = l[len(l)-1-i]
}
return l2
}
2017-11-19 14:06:01 -08:00
// PendingEvidence returns all known uncommitted evidence.
func (store *EvidenceStore) PendingEvidence() (evidence []types.Evidence) {
2017-11-19 15:43:36 -08:00
return store.ListEvidence(baseKeyPending)
}
// ListEvidence lists the evidence for the given prefix key.
// It is wrapped by PriorityEvidence and PendingEvidence for convenience.
func (store *EvidenceStore) ListEvidence(prefixKey string) (evidence []types.Evidence) {
iter := dbm.IteratePrefix(store.db, []byte(prefixKey))
for ; iter.Valid(); iter.Next() {
val := iter.Value()
2017-11-19 19:55:59 -08:00
var ei EvidenceInfo
2018-04-05 05:43:23 -07:00
err := cdc.UnmarshalBinaryBare(val, &ei)
if err != nil {
panic(err)
}
evidence = append(evidence, ei.Evidence)
2017-11-02 17:26:07 -07:00
}
return evidence
2017-11-02 17:26:07 -07:00
}
2017-11-19 15:43:36 -08:00
// GetEvidence fetches the evidence with the given height and hash.
2017-12-26 17:34:57 -08:00
func (store *EvidenceStore) GetEvidence(height int64, hash []byte) *EvidenceInfo {
2017-11-19 15:43:36 -08:00
key := keyLookupFromHeightAndHash(height, hash)
val := store.db.Get(key)
2017-11-19 19:55:59 -08:00
2017-11-19 15:43:36 -08:00
if len(val) == 0 {
return nil
}
2017-11-20 10:59:10 -08:00
var ei EvidenceInfo
2018-04-05 05:43:23 -07:00
err := cdc.UnmarshalBinaryBare(val, &ei)
if err != nil {
panic(err)
}
2017-11-20 10:59:10 -08:00
return &ei
2017-11-19 15:43:36 -08:00
}
2017-11-02 17:26:07 -07:00
// AddNewEvidence adds the given evidence to the database.
2017-11-20 10:59:10 -08:00
// It returns false if the evidence is already stored.
2017-12-26 17:34:57 -08:00
func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int64) bool {
2017-11-02 17:26:07 -07:00
// check if we already have seen it
2017-11-19 19:55:59 -08:00
ei_ := store.GetEvidence(evidence.Height(), evidence.Hash())
if ei_ != nil && ei_.Evidence != nil {
2017-11-19 20:22:25 -08:00
return false
2017-11-02 17:26:07 -07:00
}
2017-11-19 19:55:59 -08:00
ei := EvidenceInfo{
2017-11-02 17:26:07 -07:00
Committed: false,
Priority: priority,
2017-11-02 17:26:07 -07:00
Evidence: evidence,
}
2018-04-05 05:43:23 -07:00
eiBytes := cdc.MustMarshalBinaryBare(ei)
// add it to the store
2017-11-19 15:43:36 -08:00
key := keyOutqueue(evidence, priority)
store.db.Set(key, eiBytes)
2017-11-02 17:26:07 -07:00
key = keyPending(evidence)
store.db.Set(key, eiBytes)
2017-11-02 17:26:07 -07:00
2017-11-19 14:06:01 -08:00
key = keyLookup(evidence)
store.db.SetSync(key, eiBytes)
2017-11-19 20:22:25 -08:00
return true
2017-11-02 17:26:07 -07:00
}
2017-11-19 14:06:01 -08:00
// MarkEvidenceAsBroadcasted removes evidence from Outqueue.
func (store *EvidenceStore) MarkEvidenceAsBroadcasted(evidence types.Evidence) {
2017-11-19 14:06:01 -08:00
ei := store.getEvidenceInfo(evidence)
key := keyOutqueue(evidence, ei.Priority)
2017-11-02 17:26:07 -07:00
store.db.Delete(key)
}
// MarkEvidenceAsCommitted removes evidence from pending and outqueue and sets the state to committed.
2017-11-02 17:26:07 -07:00
func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) {
2017-11-18 16:57:55 -08:00
// if its committed, its been broadcast
store.MarkEvidenceAsBroadcasted(evidence)
2017-11-19 19:55:59 -08:00
pendingKey := keyPending(evidence)
store.db.Delete(pendingKey)
2017-11-02 17:26:07 -07:00
2017-11-19 14:06:01 -08:00
ei := store.getEvidenceInfo(evidence)
ei.Committed = true
2017-11-19 19:55:59 -08:00
lookupKey := keyLookup(evidence)
2018-04-05 05:43:23 -07:00
store.db.SetSync(lookupKey, cdc.MustMarshalBinaryBare(ei))
2017-11-19 14:06:01 -08:00
}
2017-11-19 15:43:36 -08:00
//---------------------------------------------------
// utils
2017-11-19 19:55:59 -08:00
func (store *EvidenceStore) getEvidenceInfo(evidence types.Evidence) EvidenceInfo {
2017-11-19 14:06:01 -08:00
key := keyLookup(evidence)
2017-11-19 19:55:59 -08:00
var ei EvidenceInfo
2017-11-02 17:26:07 -07:00
b := store.db.Get(key)
2018-04-05 05:43:23 -07:00
err := cdc.UnmarshalBinaryBare(b, &ei)
if err != nil {
panic(err)
}
2017-11-19 14:06:01 -08:00
return ei
2017-11-02 17:26:07 -07:00
}