check evidence is from validator; some cleanup

This commit is contained in:
Ethan Buchman
2017-11-03 01:50:05 -06:00
parent 10c43c9edc
commit df3f4de7c3
5 changed files with 99 additions and 36 deletions

View File

@ -42,20 +42,17 @@ func (evpool *EvidencePool) NewEvidenceChan() chan types.Evidence {
// PriorityEvidence returns the priority evidence. // PriorityEvidence returns the priority evidence.
func (evpool *EvidencePool) PriorityEvidence() []types.Evidence { func (evpool *EvidencePool) PriorityEvidence() []types.Evidence {
// TODO return evpool.evidenceStore.PriorityEvidence()
return nil
} }
// PendingEvidence returns all uncommitted evidence. // PendingEvidence returns all uncommitted evidence.
func (evpool *EvidencePool) PendingEvidence() []types.Evidence { func (evpool *EvidencePool) PendingEvidence() []types.Evidence {
// TODO return evpool.evidenceStore.PendingEvidence()
return nil
} }
// AddEvidence checks the evidence is valid and adds it to the pool. // AddEvidence checks the evidence is valid and adds it to the pool.
func (evpool *EvidencePool) AddEvidence(evidence types.Evidence) (err error) { func (evpool *EvidencePool) AddEvidence(evidence types.Evidence) (err error) {
idx := 1 // TODO added, err := evpool.evidenceStore.AddNewEvidence(evidence)
added, err := evpool.evidenceStore.AddNewEvidence(idx, evidence)
if err != nil { if err != nil {
return err return err
} else if !added { } else if !added {

View File

@ -67,20 +67,19 @@ func (evR *EvidencePoolReactor) GetChannels() []*p2p.ChannelDescriptor {
// AddPeer implements Reactor. // AddPeer implements Reactor.
func (evR *EvidencePoolReactor) AddPeer(peer p2p.Peer) { func (evR *EvidencePoolReactor) AddPeer(peer p2p.Peer) {
// first send the peer high-priority evidence // send the peer our high-priority evidence.
// the rest will be sent by the broadcastRoutine
evidence := evR.evpool.PriorityEvidence() evidence := evR.evpool.PriorityEvidence()
msg := EvidenceMessage{evidence} msg := EvidenceMessage{evidence}
success := peer.Send(EvidencePoolChannel, struct{ EvidencePoolMessage }{msg}) success := peer.Send(EvidencePoolChannel, struct{ EvidencePoolMessage }{msg})
if !success { if !success {
// TODO: remove peer ? // TODO: remove peer ?
} }
// TODO: send the remaining pending evidence
// or just let the broadcastRoutine do it ?
} }
// RemovePeer implements Reactor. // RemovePeer implements Reactor.
func (evR *EvidencePoolReactor) RemovePeer(peer p2p.Peer, reason interface{}) { func (evR *EvidencePoolReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
// nothing to do
} }
// Receive implements Reactor. // Receive implements Reactor.
@ -122,10 +121,9 @@ func (evR *EvidencePoolReactor) broadcastRoutine() {
msg := EvidenceMessage{[]types.Evidence{evidence}} msg := EvidenceMessage{[]types.Evidence{evidence}}
evR.Switch.Broadcast(EvidencePoolChannel, struct{ EvidencePoolMessage }{msg}) evR.Switch.Broadcast(EvidencePoolChannel, struct{ EvidencePoolMessage }{msg})
// NOTE: Broadcast runs asynchronously, so this should wait on the successChan // TODO: Broadcast runs asynchronously, so this should wait on the successChan
// in another routine before marking to be proper. // in another routine before marking to be proper.
idx := 1 // TODO evR.evpool.evidenceStore.MarkEvidenceAsBroadcasted(evidence)
evR.evpool.evidenceStore.MarkEvidenceAsBroadcasted(idx, evidence)
case <-ticker.C: case <-ticker.C:
// broadcast all pending evidence // broadcast all pending evidence
msg := EvidenceMessage{evR.evpool.PendingEvidence()} msg := EvidenceMessage{evR.evpool.PendingEvidence()}

View File

@ -22,16 +22,22 @@ type evidenceInfo struct {
Evidence types.Evidence Evidence types.Evidence
} }
const (
baseKeyLookup = "evidence-lookup"
baseKeyOutqueue = "evidence-outqueue"
baseKeyPending = "evidence-pending"
)
func keyLookup(evidence types.Evidence) []byte { func keyLookup(evidence types.Evidence) []byte {
return []byte(fmt.Sprintf("evidence-lookup/%d/%X", evidence.Height(), evidence.Hash())) return []byte(fmt.Sprintf("%s/%d/%X", baseKeyLookup, evidence.Height(), evidence.Hash()))
} }
func keyOutqueue(idx int, evidence types.Evidence) []byte { func keyOutqueue(evidence types.Evidence) []byte {
return []byte(fmt.Sprintf("evidence-outqueue/%d/%d/%X", idx, evidence.Height(), evidence.Hash())) return []byte(fmt.Sprintf("%s/%d/%X", baseKeyOutqueue, evidence.Height(), evidence.Hash()))
} }
func keyPending(evidence types.Evidence) []byte { func keyPending(evidence types.Evidence) []byte {
return []byte(fmt.Sprintf("evidence-pending/%d/%X", evidence.Height(), evidence.Hash())) return []byte(fmt.Sprintf("%s/%d/%X", baseKeyPending, evidence.Height(), evidence.Hash()))
} }
// EvidenceStore stores all the evidence we've seen, including // EvidenceStore stores all the evidence we've seen, including
@ -40,49 +46,85 @@ func keyPending(evidence types.Evidence) []byte {
type EvidenceStore struct { type EvidenceStore struct {
chainID string chainID string
db dbm.DB db dbm.DB
historicalValidators types.HistoricalValidators
} }
func NewEvidenceStore(chainID string, db dbm.DB) *EvidenceStore { func NewEvidenceStore(chainID string, db dbm.DB) *EvidenceStore {
return &EvidenceStore{ return &EvidenceStore{
chainID: chainID, chainID: chainID,
db: db, db: db,
// 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)
}
return evidence
}
// AddNewEvidence adds the given evidence to the database. // AddNewEvidence adds the given evidence to the database.
func (store *EvidenceStore) AddNewEvidence(idx int, evidence types.Evidence) (bool, error) { func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence) (bool, error) {
// check if we already have seen it // check if we already have seen it
key := keyLookup(evidence) key := keyLookup(evidence)
v := store.db.Get(key) v := store.db.Get(key)
if len(v) == 0 { if len(v) != 0 {
return false, nil return false, nil
} }
// verify the evidence // verify evidence consistency
if err := evidence.Verify(store.chainID); err != nil { if err := evidence.Verify(store.chainID, store.historicalValidators); err != nil {
return false, err return false, err
} }
// add it to the store // 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)
ei := evidenceInfo{ ei := evidenceInfo{
Committed: false, Committed: false,
Priority: idx, Priority: priority,
Evidence: evidence, Evidence: evidence,
} }
store.db.Set(key, wire.BinaryBytes(ei)) eiBytes := wire.BinaryBytes(ei)
key = keyOutqueue(idx, evidence) // add it to the store
store.db.Set(key, nullValue) store.db.Set(key, eiBytes)
key = keyOutqueue(evidence)
store.db.Set(key, eiBytes)
key = keyPending(evidence) key = keyPending(evidence)
store.db.Set(key, nullValue) store.db.Set(key, eiBytes)
return true, nil return true, nil
} }
// MarkEvidenceAsBroadcasted removes evidence from the outqueue. // MarkEvidenceAsBroadcasted removes evidence from the outqueue.
func (store *EvidenceStore) MarkEvidenceAsBroadcasted(idx int, evidence types.Evidence) { func (store *EvidenceStore) MarkEvidenceAsBroadcasted(evidence types.Evidence) {
key := keyOutqueue(idx, evidence) key := keyOutqueue(evidence)
store.db.Delete(key) store.db.Delete(key)
} }

View File

@ -310,9 +310,10 @@ func (s *State) validateBlock(b *types.Block) error {
} }
for _, ev := range block.Evidence.Evidences { for _, ev := range block.Evidence.Evidences {
if err := ev.Verify(s.ChainID); err != nil { if err := ev.Verify(s.ChainID, s); err != nil {
return types.NewEvidenceInvalidErr(ev, err) return types.NewEvidenceInvalidErr(ev, err)
} }
// TODO: mark evidence as committed
} }
return nil return nil

View File

@ -26,13 +26,18 @@ func (err *ErrEvidenceInvalid) Error() string {
//------------------------------------------- //-------------------------------------------
type HistoricalValidators interface {
LoadValidators(height int) *ValidatorSet
}
// Evidence represents any provable malicious activity by a validator // Evidence represents any provable malicious activity by a validator
type Evidence interface { type Evidence interface {
Height() int Height() int // height of the equivocation
Address() []byte Address() []byte // address of the equivocating validator
Hash() []byte Index() int // index of the validator in the validator set
Verify(chainID string) error Hash() []byte // hash of the evidence
Equal(Evidence) bool Verify(chainID string, vals HistoricalValidators) error // verify the evidence
Equal(Evidence) bool // check equality of evidence
String() string String() string
} }
@ -161,6 +166,11 @@ func (dve *DuplicateVoteEvidence) Address() []byte {
return dve.PubKey.Address() return dve.PubKey.Address()
} }
// Index returns the index of the validator.
func (dve *DuplicateVoteEvidence) Index() int {
return dve.VoteA.ValidatorIndex
}
// Hash returns the hash of the evidence. // Hash returns the hash of the evidence.
func (dve *DuplicateVoteEvidence) Hash() []byte { func (dve *DuplicateVoteEvidence) Hash() []byte {
return merkle.SimpleHashFromBinary(dve) return merkle.SimpleHashFromBinary(dve)
@ -168,7 +178,10 @@ func (dve *DuplicateVoteEvidence) Hash() []byte {
// Verify returns an error if the two votes aren't conflicting. // Verify returns an error if the two votes aren't conflicting.
// To be conflicting, they must be from the same validator, for the same H/R/S, but for different blocks. // To be conflicting, they must be from the same validator, for the same H/R/S, but for different blocks.
func (dve *DuplicateVoteEvidence) Verify(chainID string) error { func (dve *DuplicateVoteEvidence) Verify(chainID string, vals HistoricalValidators) error {
// TODO: verify (cs.Height - dve.Height) < MaxHeightDiff
// H/R/S must be the same // H/R/S must be the same
if dve.VoteA.Height != dve.VoteB.Height || if dve.VoteA.Height != dve.VoteB.Height ||
dve.VoteA.Round != dve.VoteB.Round || dve.VoteA.Round != dve.VoteB.Round ||
@ -198,6 +211,18 @@ func (dve *DuplicateVoteEvidence) Verify(chainID string) error {
return ErrVoteInvalidSignature return ErrVoteInvalidSignature
} }
// The address must have been an active validator at the height
height := dve.Height()
addr := dve.Address()
idx := dve.Index()
valset := vals.LoadValidators(height)
valIdx, val := valset.GetByAddress(addr)
if val == nil {
return fmt.Errorf("Address %X was not a validator at height %d", addr, height)
} else if idx != valIdx {
return fmt.Errorf("Address %X was validator %d at height %d, not %d", addr, valIdx, height, idx)
}
return nil return nil
} }