mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-18 07:31:20 +00:00
check evidence is from validator; some cleanup
This commit is contained in:
@ -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 {
|
||||||
|
@ -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()}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user