|
@ -1,14 +1,8 @@ |
|
|
package evpool |
|
|
package evpool |
|
|
|
|
|
|
|
|
import ( |
|
|
import ( |
|
|
"container/list" |
|
|
|
|
|
"fmt" |
|
|
|
|
|
"sync" |
|
|
|
|
|
"sync/atomic" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/tendermint/tmlibs/log" |
|
|
"github.com/tendermint/tmlibs/log" |
|
|
|
|
|
|
|
|
cfg "github.com/tendermint/tendermint/config" |
|
|
|
|
|
"github.com/tendermint/tendermint/types" |
|
|
"github.com/tendermint/tendermint/types" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
@ -16,25 +10,23 @@ const cacheSize = 100000 |
|
|
|
|
|
|
|
|
// EvidencePool maintains a set of valid uncommitted evidence.
|
|
|
// EvidencePool maintains a set of valid uncommitted evidence.
|
|
|
type EvidencePool struct { |
|
|
type EvidencePool struct { |
|
|
config *cfg.EvidencePoolConfig |
|
|
|
|
|
|
|
|
|
|
|
mtx sync.Mutex |
|
|
|
|
|
height int // the last block Update()'d to
|
|
|
|
|
|
evidence types.Evidences |
|
|
|
|
|
// TODO: evidenceCache
|
|
|
|
|
|
|
|
|
config *EvidencePoolConfig |
|
|
|
|
|
logger log.Logger |
|
|
|
|
|
|
|
|
// TODO: need to persist evidence so we never lose it
|
|
|
|
|
|
|
|
|
evidenceStore *EvidenceStore |
|
|
|
|
|
newEvidenceChan chan types.Evidence |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
logger log.Logger |
|
|
|
|
|
|
|
|
type EvidencePoolConfig struct { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func NewEvidencePool(config *cfg.EvidencePoolConfig, height int) *EvidencePool { |
|
|
|
|
|
|
|
|
func NewEvidencePool(config *EvidencePoolConfig, evidenceStore *EvidenceStore) *EvidencePool { |
|
|
evpool := &EvidencePool{ |
|
|
evpool := &EvidencePool{ |
|
|
config: config, |
|
|
|
|
|
height: height, |
|
|
|
|
|
logger: log.NewNopLogger(), |
|
|
|
|
|
|
|
|
config: config, |
|
|
|
|
|
logger: log.NewNopLogger(), |
|
|
|
|
|
evidenceStore: evidenceStore, |
|
|
|
|
|
newEvidenceChan: make(chan types.Evidence), |
|
|
} |
|
|
} |
|
|
evpool.initWAL() |
|
|
|
|
|
return evpool |
|
|
return evpool |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -43,160 +35,59 @@ func (evpool *EvidencePool) SetLogger(l log.Logger) { |
|
|
evpool.logger = l |
|
|
evpool.logger = l |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Evidence returns a copy of the pool's evidence.
|
|
|
|
|
|
func (evpool *EvidencePool) Evidence() types.Evidences { |
|
|
|
|
|
evpool.mtx.Lock() |
|
|
|
|
|
defer evpool.mtx.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
evCopy := make(types.Evidences, len(evpool.evidence)) |
|
|
|
|
|
for i, ev := range evpool.evidence { |
|
|
|
|
|
evCopy[i] = ev |
|
|
|
|
|
} |
|
|
|
|
|
return evCopy |
|
|
|
|
|
|
|
|
// NewEvidenceChan returns a channel on which new evidence is sent.
|
|
|
|
|
|
func (evpool *EvidencePool) NewEvidenceChan() chan types.Evidence { |
|
|
|
|
|
return evpool.newEvidenceChan |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Size returns the number of pieces of evidence in the evpool.
|
|
|
|
|
|
func (evpool *EvidencePool) Size() int { |
|
|
|
|
|
evpool.mtx.Lock() |
|
|
|
|
|
defer evpool.mtx.Unlock() |
|
|
|
|
|
return len(evpool.evidence) |
|
|
|
|
|
|
|
|
// PriorityEvidence returns the priority evidence.
|
|
|
|
|
|
func (evpool *EvidencePool) PriorityEvidence() []types.Evidence { |
|
|
|
|
|
// TODO
|
|
|
|
|
|
return nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Flush removes all evidence from the evpool
|
|
|
|
|
|
func (evpool *EvidencePool) Flush() { |
|
|
|
|
|
evpool.mtx.Lock() |
|
|
|
|
|
defer evpool.mtx.Unlock() |
|
|
|
|
|
evpool.evidence = make(types.Evidence) |
|
|
|
|
|
|
|
|
// PendingEvidence returns all uncommitted evidence.
|
|
|
|
|
|
func (evpool *EvidencePool) PendingEvidence() []types.Evidence { |
|
|
|
|
|
// TODO
|
|
|
|
|
|
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) { |
|
|
evpool.mtx.Lock() |
|
|
|
|
|
defer evpool.mtx.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
if evpool.evidence.Has(evidence) { |
|
|
|
|
|
return fmt.Errorf("Evidence already exists", "evidence", evidence) |
|
|
|
|
|
|
|
|
idx := 1 // TODO
|
|
|
|
|
|
added, err := evpool.evidenceStore.AddNewEvidence(idx, evidence) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
return err |
|
|
|
|
|
} else if !added { |
|
|
|
|
|
// evidence already known, just ignore
|
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
cs.Logger.Info("Found conflicting vote. Recording evidence", "evidence", ev) |
|
|
|
|
|
evpool.evidence = append(evpool.evidence, ev) |
|
|
|
|
|
// TODO: write to disk ? WAL ?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
evpool.logger.Info("Verified new evidence of byzantine behaviour", "evidence", evidence) |
|
|
|
|
|
|
|
|
|
|
|
evpool.newEvidenceChan <- evidence |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Update informs the evpool that the given evidence was committed and can be discarded.
|
|
|
// Update informs the evpool that the given evidence was committed and can be discarded.
|
|
|
// NOTE: this should be called *after* block is committed by consensus.
|
|
|
// NOTE: this should be called *after* block is committed by consensus.
|
|
|
func (evpool *EvidencePool) Update(height int, evidence types.Evidences) { |
|
|
|
|
|
|
|
|
func (evpool *EvidencePool) Update(height int, evidence []types.Evidence) { |
|
|
|
|
|
|
|
|
|
|
|
// First, create a lookup map of new committed evidence
|
|
|
|
|
|
|
|
|
// First, create a lookup map of txns in new txs.
|
|
|
|
|
|
evMap := make(map[string]struct{}) |
|
|
evMap := make(map[string]struct{}) |
|
|
for _, ev := range evidence { |
|
|
for _, ev := range evidence { |
|
|
evMap[string(evidence.Hash())] = struct{}{} |
|
|
|
|
|
|
|
|
evpool.evidenceStore.MarkEvidenceAsCommitted(ev) |
|
|
|
|
|
evMap[string(ev.Hash())] = struct{}{} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Set height
|
|
|
|
|
|
evpool.height = height |
|
|
|
|
|
|
|
|
|
|
|
// Remove evidence that is already committed .
|
|
|
// Remove evidence that is already committed .
|
|
|
goodEvidence := evpool.filterEvidence(evMap) |
|
|
goodEvidence := evpool.filterEvidence(evMap) |
|
|
_ = goodEvidence |
|
|
_ = goodEvidence |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
|
|
func (evpool *EvidencePool) filterTxs(blockTxsMap map[string]struct{}) []types.Tx { |
|
|
|
|
|
goodTxs := make([]types.Tx, 0, evpool.txs.Len()) |
|
|
|
|
|
for e := evpool.txs.Front(); e != nil; e = e.Next() { |
|
|
|
|
|
memTx := e.Value.(*evpoolTx) |
|
|
|
|
|
// Remove the tx if it's alredy in a block.
|
|
|
|
|
|
if _, ok := blockTxsMap[string(memTx.tx)]; ok { |
|
|
|
|
|
// remove from clist
|
|
|
|
|
|
evpool.txs.Remove(e) |
|
|
|
|
|
e.DetachPrev() |
|
|
|
|
|
|
|
|
|
|
|
// NOTE: we don't remove committed txs from the cache.
|
|
|
|
|
|
continue |
|
|
|
|
|
} |
|
|
|
|
|
// Good tx!
|
|
|
|
|
|
goodTxs = append(goodTxs, memTx.tx) |
|
|
|
|
|
} |
|
|
|
|
|
return goodTxs |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
// evpoolTx is a transaction that successfully ran
|
|
|
|
|
|
type evpoolEvidence struct { |
|
|
|
|
|
counter int64 // a simple incrementing counter
|
|
|
|
|
|
height int64 // height that this tx had been validated in
|
|
|
|
|
|
evidence types.Evidence //
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Height returns the height for this transaction
|
|
|
|
|
|
func (memTx *evpoolTx) Height() int { |
|
|
|
|
|
return int(atomic.LoadInt64(&memTx.height)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------
|
|
|
|
|
|
// TODO:
|
|
|
|
|
|
|
|
|
|
|
|
// txCache maintains a cache of evidence
|
|
|
|
|
|
type txCache struct { |
|
|
|
|
|
mtx sync.Mutex |
|
|
|
|
|
size int |
|
|
|
|
|
map_ map[string]struct{} |
|
|
|
|
|
list *list.List // to remove oldest tx when cache gets too big
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// newTxCache returns a new txCache.
|
|
|
|
|
|
func newTxCache(cacheSize int) *txCache { |
|
|
|
|
|
return &txCache{ |
|
|
|
|
|
size: cacheSize, |
|
|
|
|
|
map_: make(map[string]struct{}, cacheSize), |
|
|
|
|
|
list: list.New(), |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Reset resets the txCache to empty.
|
|
|
|
|
|
func (cache *txCache) Reset() { |
|
|
|
|
|
cache.mtx.Lock() |
|
|
|
|
|
cache.map_ = make(map[string]struct{}, cacheSize) |
|
|
|
|
|
cache.list.Init() |
|
|
|
|
|
cache.mtx.Unlock() |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Exists returns true if the given tx is cached.
|
|
|
|
|
|
func (cache *txCache) Exists(tx types.Tx) bool { |
|
|
|
|
|
cache.mtx.Lock() |
|
|
|
|
|
_, exists := cache.map_[string(tx)] |
|
|
|
|
|
cache.mtx.Unlock() |
|
|
|
|
|
return exists |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Push adds the given tx to the txCache. It returns false if tx is already in the cache.
|
|
|
|
|
|
func (cache *txCache) Push(tx types.Tx) bool { |
|
|
|
|
|
cache.mtx.Lock() |
|
|
|
|
|
defer cache.mtx.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
if _, exists := cache.map_[string(tx)]; exists { |
|
|
|
|
|
return false |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if cache.list.Len() >= cache.size { |
|
|
|
|
|
popped := cache.list.Front() |
|
|
|
|
|
poppedTx := popped.Value.(types.Tx) |
|
|
|
|
|
// NOTE: the tx may have already been removed from the map
|
|
|
|
|
|
// but deleting a non-existent element is fine
|
|
|
|
|
|
delete(cache.map_, string(poppedTx)) |
|
|
|
|
|
cache.list.Remove(popped) |
|
|
|
|
|
} |
|
|
|
|
|
cache.map_[string(tx)] = struct{}{} |
|
|
|
|
|
cache.list.PushBack(tx) |
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Remove removes the given tx from the cache.
|
|
|
|
|
|
func (cache *txCache) Remove(tx types.Tx) { |
|
|
|
|
|
cache.mtx.Lock() |
|
|
|
|
|
delete(cache.map_, string(tx)) |
|
|
|
|
|
cache.mtx.Unlock() |
|
|
|
|
|
|
|
|
func (evpool *EvidencePool) filterEvidence(blockEvidenceMap map[string]struct{}) []types.Evidence { |
|
|
|
|
|
// TODO:
|
|
|
|
|
|
return nil |
|
|
} |
|
|
} |