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