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