2017-11-02 18:26:07 -06:00
|
|
|
package evpool
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
wire "github.com/tendermint/go-wire"
|
|
|
|
"github.com/tendermint/tendermint/types"
|
|
|
|
dbm "github.com/tendermint/tmlibs/db"
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
|
|
|
"evidence-lookup"/<evidence-height>/<evidence-hash> -> evidence struct
|
|
|
|
"evidence-outqueue"/<index>/<evidence-height>/<evidence-hash> -> nil
|
|
|
|
"evidence-pending"/<evidence-height>/evidence-hash> -> nil
|
|
|
|
*/
|
|
|
|
|
|
|
|
var nullValue = []byte{0}
|
|
|
|
|
|
|
|
type evidenceInfo struct {
|
|
|
|
Committed bool
|
|
|
|
Priority int
|
|
|
|
Evidence types.Evidence
|
|
|
|
}
|
|
|
|
|
2017-11-03 01:50:05 -06:00
|
|
|
const (
|
|
|
|
baseKeyLookup = "evidence-lookup"
|
|
|
|
baseKeyOutqueue = "evidence-outqueue"
|
|
|
|
baseKeyPending = "evidence-pending"
|
|
|
|
)
|
|
|
|
|
2017-11-02 18:26:07 -06:00
|
|
|
func keyLookup(evidence types.Evidence) []byte {
|
2017-11-03 01:50:05 -06:00
|
|
|
return []byte(fmt.Sprintf("%s/%d/%X", baseKeyLookup, evidence.Height(), evidence.Hash()))
|
2017-11-02 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
2017-11-03 01:50:05 -06:00
|
|
|
func keyOutqueue(evidence types.Evidence) []byte {
|
|
|
|
return []byte(fmt.Sprintf("%s/%d/%X", baseKeyOutqueue, evidence.Height(), evidence.Hash()))
|
2017-11-02 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func keyPending(evidence types.Evidence) []byte {
|
2017-11-03 01:50:05 -06:00
|
|
|
return []byte(fmt.Sprintf("%s/%d/%X", baseKeyPending, evidence.Height(), evidence.Hash()))
|
2017-11-02 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// EvidenceStore stores all the evidence we've seen, including
|
|
|
|
// evidence that has been committed, evidence that has been seen but not broadcast,
|
|
|
|
// and evidence that has been broadcast but not yet committed.
|
|
|
|
type EvidenceStore struct {
|
|
|
|
chainID string
|
|
|
|
db dbm.DB
|
2017-11-03 01:50:05 -06:00
|
|
|
|
|
|
|
historicalValidators types.HistoricalValidators
|
2017-11-02 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewEvidenceStore(chainID string, db dbm.DB) *EvidenceStore {
|
|
|
|
return &EvidenceStore{
|
|
|
|
chainID: chainID,
|
|
|
|
db: db,
|
2017-11-03 01:50:05 -06:00
|
|
|
// TODO historicalValidators
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PriorityEvidence returns the evidence from the outqueue, sorted by highest priority.
|
|
|
|
func (store *EvidenceStore) PriorityEvidence() (evidence []types.Evidence) {
|
|
|
|
iter := store.db.IteratorPrefix([]byte(baseKeyOutqueue))
|
|
|
|
for iter.Next() {
|
|
|
|
val := iter.Value()
|
|
|
|
|
|
|
|
var ei evidenceInfo
|
|
|
|
wire.ReadBinaryBytes(val, &ei)
|
|
|
|
evidence = append(evidence, ei.Evidence)
|
|
|
|
}
|
|
|
|
// TODO: sort
|
|
|
|
return evidence
|
|
|
|
}
|
|
|
|
|
|
|
|
func (store *EvidenceStore) PendingEvidence() (evidence []types.Evidence) {
|
|
|
|
iter := store.db.IteratorPrefix([]byte(baseKeyPending))
|
|
|
|
for iter.Next() {
|
|
|
|
val := iter.Value()
|
|
|
|
|
|
|
|
var ei evidenceInfo
|
|
|
|
wire.ReadBinaryBytes(val, &ei)
|
|
|
|
evidence = append(evidence, ei.Evidence)
|
2017-11-02 18:26:07 -06:00
|
|
|
}
|
2017-11-03 01:50:05 -06:00
|
|
|
return evidence
|
2017-11-02 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddNewEvidence adds the given evidence to the database.
|
2017-11-03 01:50:05 -06:00
|
|
|
func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence) (bool, error) {
|
2017-11-02 18:26:07 -06:00
|
|
|
// check if we already have seen it
|
|
|
|
key := keyLookup(evidence)
|
|
|
|
v := store.db.Get(key)
|
2017-11-03 01:50:05 -06:00
|
|
|
if len(v) != 0 {
|
2017-11-02 18:26:07 -06:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2017-11-03 01:50:05 -06:00
|
|
|
// verify evidence consistency
|
|
|
|
if err := evidence.Verify(store.chainID, store.historicalValidators); err != nil {
|
2017-11-02 18:26:07 -06:00
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2017-11-03 01:50:05 -06:00
|
|
|
// TODO: or we let Verify return the val to avoid running this again?
|
|
|
|
valSet := store.historicalValidators.LoadValidators(evidence.Height())
|
|
|
|
_, val := valSet.GetByAddress(evidence.Address())
|
|
|
|
priority := int(val.VotingPower)
|
|
|
|
|
2017-11-02 18:26:07 -06:00
|
|
|
ei := evidenceInfo{
|
|
|
|
Committed: false,
|
2017-11-03 01:50:05 -06:00
|
|
|
Priority: priority,
|
2017-11-02 18:26:07 -06:00
|
|
|
Evidence: evidence,
|
|
|
|
}
|
2017-11-03 01:50:05 -06:00
|
|
|
eiBytes := wire.BinaryBytes(ei)
|
|
|
|
|
|
|
|
// add it to the store
|
|
|
|
store.db.Set(key, eiBytes)
|
2017-11-02 18:26:07 -06:00
|
|
|
|
2017-11-03 01:50:05 -06:00
|
|
|
key = keyOutqueue(evidence)
|
|
|
|
store.db.Set(key, eiBytes)
|
2017-11-02 18:26:07 -06:00
|
|
|
|
|
|
|
key = keyPending(evidence)
|
2017-11-03 01:50:05 -06:00
|
|
|
store.db.Set(key, eiBytes)
|
2017-11-02 18:26:07 -06:00
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarkEvidenceAsBroadcasted removes evidence from the outqueue.
|
2017-11-03 01:50:05 -06:00
|
|
|
func (store *EvidenceStore) MarkEvidenceAsBroadcasted(evidence types.Evidence) {
|
|
|
|
key := keyOutqueue(evidence)
|
2017-11-02 18:26:07 -06:00
|
|
|
store.db.Delete(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarkEvidenceAsPending removes evidence from pending and sets the state to committed.
|
|
|
|
func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) {
|
|
|
|
key := keyPending(evidence)
|
|
|
|
store.db.Delete(key)
|
|
|
|
|
|
|
|
key = keyLookup(evidence)
|
|
|
|
var ei evidenceInfo
|
|
|
|
b := store.db.Get(key)
|
|
|
|
wire.ReadBinaryBytes(b, &ei)
|
|
|
|
ei.Committed = true
|
|
|
|
store.db.Set(key, wire.BinaryBytes(ei))
|
|
|
|
}
|