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.

203 lines
5.8 KiB

6 years ago
6 years ago
6 years ago
6 years ago
  1. package evidence
  2. import (
  3. "fmt"
  4. dbm "github.com/tendermint/tendermint/libs/db"
  5. "github.com/tendermint/tendermint/types"
  6. )
  7. /*
  8. Requirements:
  9. - Valid new evidence must be persisted immediately and never forgotten
  10. - Uncommitted evidence must be continuously broadcast
  11. - Uncommitted evidence has a partial order, the evidence's priority
  12. Impl:
  13. - First commit atomically in outqueue, pending, lookup.
  14. - Once broadcast, remove from outqueue. No need to sync
  15. - Once committed, atomically remove from pending and update lookup.
  16. Schema for indexing evidence (note you need both height and hash to find a piece of evidence):
  17. "evidence-lookup"/<evidence-height>/<evidence-hash> -> EvidenceInfo
  18. "evidence-outqueue"/<priority>/<evidence-height>/<evidence-hash> -> EvidenceInfo
  19. "evidence-pending"/<evidence-height>/<evidence-hash> -> EvidenceInfo
  20. */
  21. type EvidenceInfo struct {
  22. Committed bool
  23. Priority int64
  24. Evidence types.Evidence
  25. }
  26. const (
  27. baseKeyLookup = "evidence-lookup" // all evidence
  28. baseKeyOutqueue = "evidence-outqueue" // not-yet broadcast
  29. baseKeyPending = "evidence-pending" // broadcast but not committed
  30. )
  31. func keyLookup(evidence types.Evidence) []byte {
  32. return keyLookupFromHeightAndHash(evidence.Height(), evidence.Hash())
  33. }
  34. // big endian padded hex
  35. func bE(h int64) string {
  36. return fmt.Sprintf("%0.16X", h)
  37. }
  38. func keyLookupFromHeightAndHash(height int64, hash []byte) []byte {
  39. return _key("%s/%s/%X", baseKeyLookup, bE(height), hash)
  40. }
  41. func keyOutqueue(evidence types.Evidence, priority int64) []byte {
  42. return _key("%s/%s/%s/%X", baseKeyOutqueue, bE(priority), bE(evidence.Height()), evidence.Hash())
  43. }
  44. func keyPending(evidence types.Evidence) []byte {
  45. return _key("%s/%s/%X", baseKeyPending, bE(evidence.Height()), evidence.Hash())
  46. }
  47. func _key(fmt_ string, o ...interface{}) []byte {
  48. return []byte(fmt.Sprintf(fmt_, o...))
  49. }
  50. // EvidenceStore is a store of all the evidence we've seen, including
  51. // evidence that has been committed, evidence that has been verified but not broadcast,
  52. // and evidence that has been broadcast but not yet committed.
  53. type EvidenceStore struct {
  54. db dbm.DB
  55. }
  56. func NewEvidenceStore(db dbm.DB) *EvidenceStore {
  57. return &EvidenceStore{
  58. db: db,
  59. }
  60. }
  61. // PriorityEvidence returns the evidence from the outqueue, sorted by highest priority.
  62. func (store *EvidenceStore) PriorityEvidence() (evidence []types.Evidence) {
  63. // reverse the order so highest priority is first
  64. l := store.listEvidence(baseKeyOutqueue, -1)
  65. for i, j := 0, len(l)-1; i < j; i, j = i+1, j-1 {
  66. l[i], l[j] = l[j], l[i]
  67. }
  68. return l
  69. }
  70. // PendingEvidence returns up to maxNum known, uncommitted evidence.
  71. // If maxNum is -1, all evidence is returned.
  72. func (store *EvidenceStore) PendingEvidence(maxNum int64) (evidence []types.Evidence) {
  73. return store.listEvidence(baseKeyPending, maxNum)
  74. }
  75. // listEvidence lists up to maxNum pieces of evidence for the given prefix key.
  76. // It is wrapped by PriorityEvidence and PendingEvidence for convenience.
  77. // If maxNum is -1, there's no cap on the size of returned evidence.
  78. func (store *EvidenceStore) listEvidence(prefixKey string, maxNum int64) (evidence []types.Evidence) {
  79. var count int64
  80. iter := dbm.IteratePrefix(store.db, []byte(prefixKey))
  81. defer iter.Close()
  82. for ; iter.Valid(); iter.Next() {
  83. val := iter.Value()
  84. if count == maxNum {
  85. return evidence
  86. }
  87. count++
  88. var ei EvidenceInfo
  89. err := cdc.UnmarshalBinaryBare(val, &ei)
  90. if err != nil {
  91. panic(err)
  92. }
  93. evidence = append(evidence, ei.Evidence)
  94. }
  95. return evidence
  96. }
  97. // GetEvidenceInfo fetches the EvidenceInfo with the given height and hash.
  98. // If not found, ei.Evidence is nil.
  99. func (store *EvidenceStore) GetEvidenceInfo(height int64, hash []byte) EvidenceInfo {
  100. key := keyLookupFromHeightAndHash(height, hash)
  101. val := store.db.Get(key)
  102. if len(val) == 0 {
  103. return EvidenceInfo{}
  104. }
  105. var ei EvidenceInfo
  106. err := cdc.UnmarshalBinaryBare(val, &ei)
  107. if err != nil {
  108. panic(err)
  109. }
  110. return ei
  111. }
  112. // AddNewEvidence adds the given evidence to the database.
  113. // It returns false if the evidence is already stored.
  114. func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int64) bool {
  115. // check if we already have seen it
  116. ei := store.getEvidenceInfo(evidence)
  117. if ei.Evidence != nil {
  118. return false
  119. }
  120. ei = EvidenceInfo{
  121. Committed: false,
  122. Priority: priority,
  123. Evidence: evidence,
  124. }
  125. eiBytes := cdc.MustMarshalBinaryBare(ei)
  126. // add it to the store
  127. key := keyOutqueue(evidence, priority)
  128. store.db.Set(key, eiBytes)
  129. key = keyPending(evidence)
  130. store.db.Set(key, eiBytes)
  131. key = keyLookup(evidence)
  132. store.db.SetSync(key, eiBytes)
  133. return true
  134. }
  135. // MarkEvidenceAsBroadcasted removes evidence from Outqueue.
  136. func (store *EvidenceStore) MarkEvidenceAsBroadcasted(evidence types.Evidence) {
  137. ei := store.getEvidenceInfo(evidence)
  138. if ei.Evidence == nil {
  139. // nothing to do; we did not store the evidence yet (AddNewEvidence):
  140. return
  141. }
  142. // remove from the outqueue
  143. key := keyOutqueue(evidence, ei.Priority)
  144. store.db.Delete(key)
  145. }
  146. // MarkEvidenceAsCommitted removes evidence from pending and outqueue and sets the state to committed.
  147. func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) {
  148. // if its committed, its been broadcast
  149. store.MarkEvidenceAsBroadcasted(evidence)
  150. pendingKey := keyPending(evidence)
  151. store.db.Delete(pendingKey)
  152. // committed EvidenceInfo doens't need priority
  153. ei := EvidenceInfo{
  154. Committed: true,
  155. Evidence: evidence,
  156. Priority: 0,
  157. }
  158. lookupKey := keyLookup(evidence)
  159. store.db.SetSync(lookupKey, cdc.MustMarshalBinaryBare(ei))
  160. }
  161. //---------------------------------------------------
  162. // utils
  163. // getEvidenceInfo is convenience for calling GetEvidenceInfo if we have the full evidence.
  164. func (store *EvidenceStore) getEvidenceInfo(evidence types.Evidence) EvidenceInfo {
  165. return store.GetEvidenceInfo(evidence.Height(), evidence.Hash())
  166. }