You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

395 lines
12 KiB

7 years ago
7 years ago
7 years ago
7 years ago
lint: Enable Golint (#4212) * Fix many golint errors * Fix golint errors in the 'lite' package * Don't export Pool.store * Fix typo * Revert unwanted changes * Fix errors in counter package * Fix linter errors in kvstore package * Fix linter error in example package * Fix error in tests package * Fix linter errors in v2 package * Fix linter errors in consensus package * Fix linter errors in evidence package * Fix linter error in fail package * Fix linter errors in query package * Fix linter errors in core package * Fix linter errors in node package * Fix linter errors in mempool package * Fix linter error in conn package * Fix linter errors in pex package * Rename PEXReactor export to Reactor * Fix linter errors in trust package * Fix linter errors in upnp package * Fix linter errors in p2p package * Fix linter errors in proxy package * Fix linter errors in mock_test package * Fix linter error in client_test package * Fix linter errors in coretypes package * Fix linter errors in coregrpc package * Fix linter errors in rpcserver package * Fix linter errors in rpctypes package * Fix linter errors in rpctest package * Fix linter error in json2wal script * Fix linter error in wal2json script * Fix linter errors in kv package * Fix linter error in state package * Fix linter error in grpc_client * Fix linter errors in types package * Fix linter error in version package * Fix remaining errors * Address review comments * Fix broken tests * Reconcile package coregrpc * Fix golangci bot error * Fix new golint errors * Fix broken reference * Enable golint linter * minor changes to bring golint into line * fix failing test * fix pex reactor naming * address PR comments
5 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
lint: Enable Golint (#4212) * Fix many golint errors * Fix golint errors in the 'lite' package * Don't export Pool.store * Fix typo * Revert unwanted changes * Fix errors in counter package * Fix linter errors in kvstore package * Fix linter error in example package * Fix error in tests package * Fix linter errors in v2 package * Fix linter errors in consensus package * Fix linter errors in evidence package * Fix linter error in fail package * Fix linter errors in query package * Fix linter errors in core package * Fix linter errors in node package * Fix linter errors in mempool package * Fix linter error in conn package * Fix linter errors in pex package * Rename PEXReactor export to Reactor * Fix linter errors in trust package * Fix linter errors in upnp package * Fix linter errors in p2p package * Fix linter errors in proxy package * Fix linter errors in mock_test package * Fix linter error in client_test package * Fix linter errors in coretypes package * Fix linter errors in coregrpc package * Fix linter errors in rpcserver package * Fix linter errors in rpctypes package * Fix linter errors in rpctest package * Fix linter error in json2wal script * Fix linter error in wal2json script * Fix linter errors in kv package * Fix linter error in state package * Fix linter error in grpc_client * Fix linter errors in types package * Fix linter error in version package * Fix remaining errors * Address review comments * Fix broken tests * Reconcile package coregrpc * Fix golangci bot error * Fix new golint errors * Fix broken reference * Enable golint linter * minor changes to bring golint into line * fix failing test * fix pex reactor naming * address PR comments
5 years ago
7 years ago
7 years ago
lint: Enable Golint (#4212) * Fix many golint errors * Fix golint errors in the 'lite' package * Don't export Pool.store * Fix typo * Revert unwanted changes * Fix errors in counter package * Fix linter errors in kvstore package * Fix linter error in example package * Fix error in tests package * Fix linter errors in v2 package * Fix linter errors in consensus package * Fix linter errors in evidence package * Fix linter error in fail package * Fix linter errors in query package * Fix linter errors in core package * Fix linter errors in node package * Fix linter errors in mempool package * Fix linter error in conn package * Fix linter errors in pex package * Rename PEXReactor export to Reactor * Fix linter errors in trust package * Fix linter errors in upnp package * Fix linter errors in p2p package * Fix linter errors in proxy package * Fix linter errors in mock_test package * Fix linter error in client_test package * Fix linter errors in coretypes package * Fix linter errors in coregrpc package * Fix linter errors in rpcserver package * Fix linter errors in rpctypes package * Fix linter errors in rpctest package * Fix linter error in json2wal script * Fix linter error in wal2json script * Fix linter errors in kv package * Fix linter error in state package * Fix linter error in grpc_client * Fix linter errors in types package * Fix linter error in version package * Fix remaining errors * Address review comments * Fix broken tests * Reconcile package coregrpc * Fix golangci bot error * Fix new golint errors * Fix broken reference * Enable golint linter * minor changes to bring golint into line * fix failing test * fix pex reactor naming * address PR comments
5 years ago
7 years ago
  1. package evidence
  2. import (
  3. "errors"
  4. "fmt"
  5. "sync"
  6. "time"
  7. "github.com/gogo/protobuf/proto"
  8. gogotypes "github.com/gogo/protobuf/types"
  9. dbm "github.com/tendermint/tm-db"
  10. clist "github.com/tendermint/tendermint/libs/clist"
  11. "github.com/tendermint/tendermint/libs/log"
  12. tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
  13. sm "github.com/tendermint/tendermint/state"
  14. "github.com/tendermint/tendermint/types"
  15. )
  16. const (
  17. baseKeyCommitted = byte(0x00)
  18. baseKeyPending = byte(0x01)
  19. )
  20. // Pool maintains a pool of valid evidence to be broadcasted and committed
  21. type Pool struct {
  22. logger log.Logger
  23. evidenceStore dbm.DB
  24. evidenceList *clist.CList // concurrent linked-list of evidence
  25. // needed to load validators to verify evidence
  26. stateDB StateStore
  27. // needed to load headers to verify evidence
  28. blockStore BlockStore
  29. mtx sync.Mutex
  30. // latest state
  31. state sm.State
  32. }
  33. // NewPool creates an evidence pool. If using an existing evidence store,
  34. // it will add all pending evidence to the concurrent list.
  35. func NewPool(evidenceDB dbm.DB, stateDB StateStore, blockStore BlockStore) (*Pool, error) {
  36. var (
  37. state = stateDB.LoadState()
  38. )
  39. pool := &Pool{
  40. stateDB: stateDB,
  41. blockStore: blockStore,
  42. state: state,
  43. logger: log.NewNopLogger(),
  44. evidenceStore: evidenceDB,
  45. evidenceList: clist.New(),
  46. }
  47. // if pending evidence already in db, in event of prior failure, then load it back to the evidenceList
  48. evList := pool.AllPendingEvidence()
  49. for _, ev := range evList {
  50. pool.evidenceList.PushBack(ev)
  51. }
  52. return pool, nil
  53. }
  54. // PendingEvidence is used primarily as part of block proposal and returns up to maxNum of uncommitted evidence.
  55. // If maxNum is -1, all evidence is returned. Pending evidence is prioritized based on time.
  56. func (evpool *Pool) PendingEvidence(maxNum uint32) []types.Evidence {
  57. evpool.removeExpiredPendingEvidence()
  58. evidence, err := evpool.listEvidence(baseKeyPending, int64(maxNum))
  59. if err != nil {
  60. evpool.logger.Error("Unable to retrieve pending evidence", "err", err)
  61. }
  62. return evidence
  63. }
  64. // AllPendingEvidence returns all evidence ready to be proposed and committed.
  65. func (evpool *Pool) AllPendingEvidence() []types.Evidence {
  66. evpool.removeExpiredPendingEvidence()
  67. evidence, err := evpool.listEvidence(baseKeyPending, -1)
  68. if err != nil {
  69. evpool.logger.Error("Unable to retrieve pending evidence", "err", err)
  70. }
  71. return evidence
  72. }
  73. // Update uses the latest block & state to update any evidence that has been committed, to prune all expired evidence
  74. func (evpool *Pool) Update(block *types.Block, state sm.State) {
  75. // sanity check
  76. if state.LastBlockHeight != block.Height {
  77. panic(fmt.Sprintf("Failed EvidencePool.Update sanity check: got state.Height=%d with block.Height=%d",
  78. state.LastBlockHeight,
  79. block.Height,
  80. ),
  81. )
  82. }
  83. // update the state
  84. evpool.updateState(state)
  85. // remove evidence from pending and mark committed
  86. evpool.MarkEvidenceAsCommitted(block.Height, block.Evidence.Evidence)
  87. // prune pending, committed and potential evidence and polc's periodically
  88. if block.Height%state.ConsensusParams.Evidence.MaxAgeNumBlocks == 0 {
  89. evpool.logger.Debug("Pruning expired evidence")
  90. // NOTE: As this is periodic, this implies that there may be some pending evidence in the
  91. // db that have already expired. However, expired evidence will also be removed whenever
  92. // PendingEvidence() is called ensuring that no expired evidence is proposed.
  93. evpool.removeExpiredPendingEvidence()
  94. }
  95. }
  96. // AddEvidence checks the evidence is valid and adds it to the pool.
  97. func (evpool *Pool) AddEvidence(ev types.Evidence) error {
  98. evpool.logger.Debug("Attempting to add evidence", "ev", ev)
  99. if evpool.Has(ev) {
  100. return nil
  101. }
  102. // 1) Verify against state.
  103. if err := evpool.verify(ev); err != nil {
  104. return types.NewErrEvidenceInvalid(ev, err)
  105. }
  106. // 2) Save to store.
  107. if err := evpool.addPendingEvidence(ev); err != nil {
  108. return fmt.Errorf("database error when adding evidence: %v", err)
  109. }
  110. // 3) Add evidence to clist.
  111. evpool.evidenceList.PushBack(ev)
  112. evpool.logger.Info("Verified new evidence of byzantine behavior", "evidence", ev)
  113. return nil
  114. }
  115. // Verify verifies the evidence against the node's (or evidence pool's) state. More specifically, to validate
  116. // evidence against state is to validate it against the nodes own header and validator set for that height. This ensures
  117. // as well as meeting the evidence's own validation rules, that the evidence hasn't expired, that the validator is still
  118. // bonded and that the evidence can be committed to the chain.
  119. func (evpool *Pool) Verify(evidence types.Evidence) error {
  120. if evpool.IsCommitted(evidence) {
  121. return errors.New("evidence was already committed")
  122. }
  123. // We have already verified this piece of evidence - no need to do it again
  124. if evpool.IsPending(evidence) {
  125. return nil
  126. }
  127. return evpool.verify(evidence)
  128. }
  129. func (evpool *Pool) verify(evidence types.Evidence) error {
  130. return VerifyEvidence(evidence, evpool.State(), evpool.stateDB, evpool.blockStore)
  131. }
  132. // MarkEvidenceAsCommitted marks all the evidence as committed and removes it
  133. // from the queue.
  134. func (evpool *Pool) MarkEvidenceAsCommitted(height int64, evidence []types.Evidence) {
  135. // make a map of committed evidence to remove from the clist
  136. blockEvidenceMap := make(map[string]struct{})
  137. for _, ev := range evidence {
  138. // As the evidence is stored in the block store we only need to record the height that it was saved at.
  139. key := keyCommitted(ev)
  140. h := gogotypes.Int64Value{Value: height}
  141. evBytes, err := proto.Marshal(&h)
  142. if err != nil {
  143. panic(err)
  144. }
  145. if err := evpool.evidenceStore.Set(key, evBytes); err != nil {
  146. evpool.logger.Error("Unable to add committed evidence", "err", err)
  147. // if we can't move evidence to committed then don't remove the evidence from pending
  148. continue
  149. }
  150. // if pending, remove from that bucket, remember not all evidence has been seen before
  151. if evpool.IsPending(ev) {
  152. evpool.removePendingEvidence(ev)
  153. blockEvidenceMap[evMapKey(ev)] = struct{}{}
  154. }
  155. }
  156. // remove committed evidence from the clist
  157. if len(blockEvidenceMap) != 0 {
  158. evpool.removeEvidenceFromList(blockEvidenceMap)
  159. }
  160. }
  161. // Has checks whether the evidence exists either pending or already committed
  162. func (evpool *Pool) Has(evidence types.Evidence) bool {
  163. return evpool.IsPending(evidence) || evpool.IsCommitted(evidence)
  164. }
  165. // IsEvidenceExpired checks whether evidence is past the maximum age where it can be used
  166. func (evpool *Pool) IsEvidenceExpired(evidence types.Evidence) bool {
  167. return evpool.IsExpired(evidence.Height(), evidence.Time())
  168. }
  169. // IsExpired checks whether evidence or a polc is expired by checking whether a height and time is older
  170. // than set by the evidence consensus parameters
  171. func (evpool *Pool) IsExpired(height int64, time time.Time) bool {
  172. var (
  173. params = evpool.State().ConsensusParams.Evidence
  174. ageDuration = evpool.State().LastBlockTime.Sub(time)
  175. ageNumBlocks = evpool.State().LastBlockHeight - height
  176. )
  177. return ageNumBlocks > params.MaxAgeNumBlocks &&
  178. ageDuration > params.MaxAgeDuration
  179. }
  180. // IsCommitted returns true if we have already seen this exact evidence and it is already marked as committed.
  181. func (evpool *Pool) IsCommitted(evidence types.Evidence) bool {
  182. key := keyCommitted(evidence)
  183. ok, err := evpool.evidenceStore.Has(key)
  184. if err != nil {
  185. evpool.logger.Error("Unable to find committed evidence", "err", err)
  186. }
  187. return ok
  188. }
  189. // IsPending checks whether the evidence is already pending. DB errors are passed to the logger.
  190. func (evpool *Pool) IsPending(evidence types.Evidence) bool {
  191. key := keyPending(evidence)
  192. ok, err := evpool.evidenceStore.Has(key)
  193. if err != nil {
  194. evpool.logger.Error("Unable to find pending evidence", "err", err)
  195. }
  196. return ok
  197. }
  198. // EvidenceFront goes to the first evidence in the clist
  199. func (evpool *Pool) EvidenceFront() *clist.CElement {
  200. return evpool.evidenceList.Front()
  201. }
  202. // EvidenceWaitChan is a channel that closes once the first evidence in the list is there. i.e Front is not nil
  203. func (evpool *Pool) EvidenceWaitChan() <-chan struct{} {
  204. return evpool.evidenceList.WaitChan()
  205. }
  206. // SetLogger sets the Logger.
  207. func (evpool *Pool) SetLogger(l log.Logger) {
  208. evpool.logger = l
  209. }
  210. // State returns the current state of the evpool.
  211. func (evpool *Pool) State() sm.State {
  212. evpool.mtx.Lock()
  213. defer evpool.mtx.Unlock()
  214. return evpool.state
  215. }
  216. func (evpool *Pool) addPendingEvidence(evidence types.Evidence) error {
  217. evi, err := types.EvidenceToProto(evidence)
  218. if err != nil {
  219. return fmt.Errorf("unable to convert to proto, err: %w", err)
  220. }
  221. evBytes, err := proto.Marshal(evi)
  222. if err != nil {
  223. return fmt.Errorf("unable to marshal evidence: %w", err)
  224. }
  225. key := keyPending(evidence)
  226. return evpool.evidenceStore.Set(key, evBytes)
  227. }
  228. func (evpool *Pool) removePendingEvidence(evidence types.Evidence) {
  229. key := keyPending(evidence)
  230. if err := evpool.evidenceStore.Delete(key); err != nil {
  231. evpool.logger.Error("Unable to delete pending evidence", "err", err)
  232. } else {
  233. evpool.logger.Info("Deleted pending evidence", "evidence", evidence)
  234. }
  235. }
  236. // listEvidence lists up to maxNum pieces of evidence for the given prefix key.
  237. // If maxNum is -1, there's no cap on the size of returned evidence.
  238. func (evpool *Pool) listEvidence(prefixKey byte, maxNum int64) ([]types.Evidence, error) {
  239. var count int64
  240. var evidence []types.Evidence
  241. iter, err := dbm.IteratePrefix(evpool.evidenceStore, []byte{prefixKey})
  242. if err != nil {
  243. return nil, fmt.Errorf("database error: %v", err)
  244. }
  245. defer iter.Close()
  246. for ; iter.Valid(); iter.Next() {
  247. if count == maxNum {
  248. return evidence, nil
  249. }
  250. count++
  251. val := iter.Value()
  252. var (
  253. ev types.Evidence
  254. evpb tmproto.Evidence
  255. )
  256. err := proto.Unmarshal(val, &evpb)
  257. if err != nil {
  258. return nil, err
  259. }
  260. ev, err = types.EvidenceFromProto(&evpb)
  261. if err != nil {
  262. return nil, err
  263. }
  264. evidence = append(evidence, ev)
  265. }
  266. return evidence, nil
  267. }
  268. func (evpool *Pool) removeExpiredPendingEvidence() {
  269. iter, err := dbm.IteratePrefix(evpool.evidenceStore, []byte{baseKeyPending})
  270. if err != nil {
  271. evpool.logger.Error("Unable to iterate over pending evidence", "err", err)
  272. return
  273. }
  274. defer iter.Close()
  275. blockEvidenceMap := make(map[string]struct{})
  276. for ; iter.Valid(); iter.Next() {
  277. evBytes := iter.Value()
  278. var (
  279. ev types.Evidence
  280. evpb tmproto.Evidence
  281. )
  282. err := proto.Unmarshal(evBytes, &evpb)
  283. if err != nil {
  284. evpool.logger.Error("Unable to unmarshal Evidence", "err", err)
  285. continue
  286. }
  287. ev, err = types.EvidenceFromProto(&evpb)
  288. if err != nil {
  289. evpool.logger.Error("Error in transition evidence from protobuf", "err", err)
  290. continue
  291. }
  292. if !evpool.IsExpired(ev.Height()-1, ev.Time()) {
  293. if len(blockEvidenceMap) != 0 {
  294. evpool.removeEvidenceFromList(blockEvidenceMap)
  295. }
  296. return
  297. }
  298. evpool.removePendingEvidence(ev)
  299. blockEvidenceMap[evMapKey(ev)] = struct{}{}
  300. }
  301. }
  302. func (evpool *Pool) removeEvidenceFromList(
  303. blockEvidenceMap map[string]struct{}) {
  304. for e := evpool.evidenceList.Front(); e != nil; e = e.Next() {
  305. // Remove from clist
  306. ev := e.Value.(types.Evidence)
  307. if _, ok := blockEvidenceMap[evMapKey(ev)]; ok {
  308. evpool.evidenceList.Remove(e)
  309. e.DetachPrev()
  310. }
  311. }
  312. }
  313. func (evpool *Pool) updateState(state sm.State) {
  314. evpool.mtx.Lock()
  315. defer evpool.mtx.Unlock()
  316. evpool.state = state
  317. }
  318. func evMapKey(ev types.Evidence) string {
  319. return string(ev.Hash())
  320. }
  321. // big endian padded hex
  322. func bE(h int64) string {
  323. return fmt.Sprintf("%0.16X", h)
  324. }
  325. func keyCommitted(evidence types.Evidence) []byte {
  326. return append([]byte{baseKeyCommitted}, keySuffix(evidence)...)
  327. }
  328. func keyPending(evidence types.Evidence) []byte {
  329. return append([]byte{baseKeyPending}, keySuffix(evidence)...)
  330. }
  331. func keySuffix(evidence types.Evidence) []byte {
  332. return []byte(fmt.Sprintf("%s/%X", bE(evidence.Height()), evidence.Hash()))
  333. }