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.

176 lines
5.4 KiB

  1. package evpool
  2. import (
  3. "fmt"
  4. wire "github.com/tendermint/go-wire"
  5. "github.com/tendermint/tendermint/types"
  6. dbm "github.com/tendermint/tmlibs/db"
  7. )
  8. /*
  9. Requirements:
  10. - Valid new evidence must be persisted immediately and never forgotten
  11. - Uncommitted evidence must be continuously broadcast
  12. - Uncommitted evidence has a partial order, the evidence's priority
  13. Impl:
  14. - First commit atomically in outqueue, pending, lookup.
  15. - Once broadcast, remove from outqueue. No need to sync
  16. - Once committed, atomically remove from pending and update lookup.
  17. - TODO: If we crash after committed but before removing/updating,
  18. we'll be stuck broadcasting evidence we never know we committed.
  19. so either share the state db and atomically MarkCommitted
  20. with ApplyBlock, or check all outqueue/pending on Start to see if its committed
  21. Schema for indexing evidence (note you need both height and hash to find a piece of evidence):
  22. "evidence-lookup"/<evidence-height>/<evidence-hash> -> evidenceInfo
  23. "evidence-outqueue"/<priority>/<evidence-height>/<evidence-hash> -> evidenceInfo
  24. "evidence-pending"/<evidence-height>/<evidence-hash> -> evidenceInfo
  25. */
  26. type evidenceInfo struct {
  27. Committed bool
  28. Priority int
  29. Evidence types.Evidence
  30. }
  31. const (
  32. baseKeyLookup = "evidence-lookup" // all evidence
  33. baseKeyOutqueue = "evidence-outqueue" // not-yet broadcast
  34. baseKeyPending = "evidence-pending" // broadcast but not committed
  35. )
  36. func keyLookup(evidence types.Evidence) []byte {
  37. return keyLookupFromHeightAndHash(evidence.Height(), evidence.Hash())
  38. }
  39. func keyLookupFromHeightAndHash(height int, hash []byte) []byte {
  40. return _key("%s/%d/%X", baseKeyLookup, height, hash)
  41. }
  42. func keyOutqueue(evidence types.Evidence, priority int) []byte {
  43. return _key("%s/%d/%d/%X", baseKeyOutqueue, priority, evidence.Height(), evidence.Hash())
  44. }
  45. func keyPending(evidence types.Evidence) []byte {
  46. return _key("%s/%d/%X", baseKeyPending, evidence.Height(), evidence.Hash())
  47. }
  48. func _key(fmt_ string, o ...interface{}) []byte {
  49. return []byte(fmt.Sprintf(fmt_, o...))
  50. }
  51. // EvidenceStore is a store of all the evidence we've seen, including
  52. // evidence that has been committed, evidence that has been verified but not broadcast,
  53. // and evidence that has been broadcast but not yet committed.
  54. type EvidenceStore struct {
  55. db dbm.DB
  56. }
  57. func NewEvidenceStore(db dbm.DB) *EvidenceStore {
  58. return &EvidenceStore{
  59. db: db,
  60. }
  61. }
  62. // PriorityEvidence returns the evidence from the outqueue, sorted by highest priority.
  63. func (store *EvidenceStore) PriorityEvidence() (evidence []types.Evidence) {
  64. // TODO: revert order for highest first
  65. return store.ListEvidence(baseKeyOutqueue)
  66. }
  67. // PendingEvidence returns all known uncommitted evidence.
  68. func (store *EvidenceStore) PendingEvidence() (evidence []types.Evidence) {
  69. return store.ListEvidence(baseKeyPending)
  70. }
  71. // ListEvidence lists the evidence for the given prefix key.
  72. // It is wrapped by PriorityEvidence and PendingEvidence for convenience.
  73. func (store *EvidenceStore) ListEvidence(prefixKey string) (evidence []types.Evidence) {
  74. iter := store.db.IteratorPrefix([]byte(prefixKey))
  75. for iter.Next() {
  76. val := iter.Value()
  77. var ei evidenceInfo
  78. wire.ReadBinaryBytes(val, &ei)
  79. evidence = append(evidence, ei.Evidence)
  80. }
  81. return evidence
  82. }
  83. // GetEvidence fetches the evidence with the given height and hash.
  84. func (store *EvidenceStore) GetEvidence(height int, hash []byte) types.Evidence {
  85. key := keyLookupFromHeightAndHash(height, hash)
  86. val := store.db.Get(key)
  87. if len(val) == 0 {
  88. return nil
  89. }
  90. var ei evidenceInfo
  91. wire.ReadBinaryBytes(val, &ei)
  92. return ei.Evidence
  93. }
  94. // AddNewEvidence adds the given evidence to the database.
  95. func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int) (bool, error) {
  96. // check if we already have seen it
  97. ev := store.GetEvidence(evidence.Height(), evidence.Hash())
  98. if ev != nil {
  99. return false, nil
  100. }
  101. ei := evidenceInfo{
  102. Committed: false,
  103. Priority: priority,
  104. Evidence: evidence,
  105. }
  106. eiBytes := wire.BinaryBytes(ei)
  107. // add it to the store
  108. key := keyOutqueue(evidence, priority)
  109. store.db.Set(key, eiBytes)
  110. key = keyPending(evidence)
  111. store.db.Set(key, eiBytes)
  112. key = keyLookup(evidence)
  113. store.db.SetSync(key, eiBytes)
  114. return true, nil
  115. }
  116. // MarkEvidenceAsBroadcasted removes evidence from Outqueue.
  117. func (store *EvidenceStore) MarkEvidenceAsBroadcasted(evidence types.Evidence) {
  118. ei := store.getEvidenceInfo(evidence)
  119. key := keyOutqueue(evidence, ei.Priority)
  120. store.db.Delete(key)
  121. }
  122. // MarkEvidenceAsPending removes evidence from pending and outqueue and sets the state to committed.
  123. func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) {
  124. // if its committed, its been broadcast
  125. store.MarkEvidenceAsBroadcasted(evidence)
  126. key := keyPending(evidence)
  127. store.db.Delete(key)
  128. ei := store.getEvidenceInfo(evidence)
  129. ei.Committed = true
  130. // TODO: we should use the state db and db.Sync in state.Save instead.
  131. // Else, if we call this before state.Save, we may never mark committed evidence as committed.
  132. // Else, if we call this after state.Save, we may get stuck broadcasting evidence we never know we committed.
  133. store.db.SetSync(key, wire.BinaryBytes(ei))
  134. }
  135. //---------------------------------------------------
  136. // utils
  137. func (store *EvidenceStore) getEvidenceInfo(evidence types.Evidence) evidenceInfo {
  138. key := keyLookup(evidence)
  139. var ei evidenceInfo
  140. b := store.db.Get(key)
  141. wire.ReadBinaryBytes(b, &ei)
  142. return ei
  143. }