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  }