github.com/evdatsion/aphelion-dpos-bft@v0.32.1/evidence/pool.go (about) 1 package evidence 2 3 import ( 4 "fmt" 5 "sync" 6 7 clist "github.com/evdatsion/aphelion-dpos-bft/libs/clist" 8 dbm "github.com/evdatsion/aphelion-dpos-bft/libs/db" 9 "github.com/evdatsion/aphelion-dpos-bft/libs/log" 10 11 sm "github.com/evdatsion/aphelion-dpos-bft/state" 12 "github.com/evdatsion/aphelion-dpos-bft/types" 13 ) 14 15 // EvidencePool maintains a pool of valid evidence 16 // in an EvidenceStore. 17 type EvidencePool struct { 18 logger log.Logger 19 20 evidenceStore *EvidenceStore 21 evidenceList *clist.CList // concurrent linked-list of evidence 22 23 // needed to load validators to verify evidence 24 stateDB dbm.DB 25 26 // latest state 27 mtx sync.Mutex 28 state sm.State 29 } 30 31 func NewEvidencePool(stateDB, evidenceDB dbm.DB) *EvidencePool { 32 evidenceStore := NewEvidenceStore(evidenceDB) 33 evpool := &EvidencePool{ 34 stateDB: stateDB, 35 state: sm.LoadState(stateDB), 36 logger: log.NewNopLogger(), 37 evidenceStore: evidenceStore, 38 evidenceList: clist.New(), 39 } 40 return evpool 41 } 42 43 func (evpool *EvidencePool) EvidenceFront() *clist.CElement { 44 return evpool.evidenceList.Front() 45 } 46 47 func (evpool *EvidencePool) EvidenceWaitChan() <-chan struct{} { 48 return evpool.evidenceList.WaitChan() 49 } 50 51 // SetLogger sets the Logger. 52 func (evpool *EvidencePool) SetLogger(l log.Logger) { 53 evpool.logger = l 54 } 55 56 // PriorityEvidence returns the priority evidence. 57 func (evpool *EvidencePool) PriorityEvidence() []types.Evidence { 58 return evpool.evidenceStore.PriorityEvidence() 59 } 60 61 // PendingEvidence returns up to maxNum uncommitted evidence. 62 // If maxNum is -1, all evidence is returned. 63 func (evpool *EvidencePool) PendingEvidence(maxNum int64) []types.Evidence { 64 return evpool.evidenceStore.PendingEvidence(maxNum) 65 } 66 67 // State returns the current state of the evpool. 68 func (evpool *EvidencePool) State() sm.State { 69 evpool.mtx.Lock() 70 defer evpool.mtx.Unlock() 71 return evpool.state 72 } 73 74 // Update loads the latest 75 func (evpool *EvidencePool) Update(block *types.Block, state sm.State) { 76 77 // sanity check 78 if state.LastBlockHeight != block.Height { 79 panic(fmt.Sprintf("Failed EvidencePool.Update sanity check: got state.Height=%d with block.Height=%d", state.LastBlockHeight, block.Height)) 80 } 81 82 // update the state 83 evpool.mtx.Lock() 84 evpool.state = state 85 evpool.mtx.Unlock() 86 87 // remove evidence from pending and mark committed 88 evpool.MarkEvidenceAsCommitted(block.Height, block.Evidence.Evidence) 89 } 90 91 // AddEvidence checks the evidence is valid and adds it to the pool. 92 func (evpool *EvidencePool) AddEvidence(evidence types.Evidence) (err error) { 93 94 // TODO: check if we already have evidence for this 95 // validator at this height so we dont get spammed 96 97 if err := sm.VerifyEvidence(evpool.stateDB, evpool.State(), evidence); err != nil { 98 return err 99 } 100 101 // fetch the validator and return its voting power as its priority 102 // TODO: something better ? 103 valset, _ := sm.LoadValidators(evpool.stateDB, evidence.Height()) 104 _, val := valset.GetByAddress(evidence.Address()) 105 priority := val.VotingPower 106 107 added := evpool.evidenceStore.AddNewEvidence(evidence, priority) 108 if !added { 109 // evidence already known, just ignore 110 return 111 } 112 113 evpool.logger.Info("Verified new evidence of byzantine behaviour", "evidence", evidence) 114 115 // add evidence to clist 116 evpool.evidenceList.PushBack(evidence) 117 118 return nil 119 } 120 121 // MarkEvidenceAsCommitted marks all the evidence as committed and removes it from the queue. 122 func (evpool *EvidencePool) MarkEvidenceAsCommitted(height int64, evidence []types.Evidence) { 123 // make a map of committed evidence to remove from the clist 124 blockEvidenceMap := make(map[string]struct{}) 125 for _, ev := range evidence { 126 evpool.evidenceStore.MarkEvidenceAsCommitted(ev) 127 blockEvidenceMap[evMapKey(ev)] = struct{}{} 128 } 129 130 // remove committed evidence from the clist 131 maxAge := evpool.State().ConsensusParams.Evidence.MaxAge 132 evpool.removeEvidence(height, maxAge, blockEvidenceMap) 133 134 } 135 136 // IsCommitted returns true if we have already seen this exact evidence and it is already marked as committed. 137 func (evpool *EvidencePool) IsCommitted(evidence types.Evidence) bool { 138 ei := evpool.evidenceStore.getEvidenceInfo(evidence) 139 return ei.Evidence != nil && ei.Committed 140 } 141 142 func (evpool *EvidencePool) removeEvidence(height, maxAge int64, blockEvidenceMap map[string]struct{}) { 143 for e := evpool.evidenceList.Front(); e != nil; e = e.Next() { 144 ev := e.Value.(types.Evidence) 145 146 // Remove the evidence if it's already in a block 147 // or if it's now too old. 148 if _, ok := blockEvidenceMap[evMapKey(ev)]; ok || 149 ev.Height() < height-maxAge { 150 151 // remove from clist 152 evpool.evidenceList.Remove(e) 153 e.DetachPrev() 154 } 155 } 156 } 157 158 func evMapKey(ev types.Evidence) string { 159 return string(ev.Hash()) 160 }