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.

194 lines
5.5 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package evidence
  2. import (
  3. "fmt"
  4. "github.com/tendermint/tendermint/types"
  5. dbm "github.com/tendermint/tmlibs/db"
  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. - TODO: If we crash after committed but before removing/updating,
  17. we'll be stuck broadcasting evidence we never know we committed.
  18. so either share the state db and atomically MarkCommitted
  19. with ApplyBlock, or check all outqueue/pending on Start to see if its committed
  20. Schema for indexing evidence (note you need both height and hash to find a piece of evidence):
  21. "evidence-lookup"/<evidence-height>/<evidence-hash> -> EvidenceInfo
  22. "evidence-outqueue"/<priority>/<evidence-height>/<evidence-hash> -> EvidenceInfo
  23. "evidence-pending"/<evidence-height>/<evidence-hash> -> EvidenceInfo
  24. */
  25. type EvidenceInfo struct {
  26. Committed bool
  27. Priority int64
  28. Evidence types.Evidence
  29. }
  30. const (
  31. baseKeyLookup = "evidence-lookup" // all evidence
  32. baseKeyOutqueue = "evidence-outqueue" // not-yet broadcast
  33. baseKeyPending = "evidence-pending" // broadcast but not committed
  34. )
  35. func keyLookup(evidence types.Evidence) []byte {
  36. return keyLookupFromHeightAndHash(evidence.Height(), evidence.Hash())
  37. }
  38. // big endian padded hex
  39. func bE(h int64) string {
  40. return fmt.Sprintf("%0.16X", h)
  41. }
  42. func keyLookupFromHeightAndHash(height int64, hash []byte) []byte {
  43. return _key("%s/%s/%X", baseKeyLookup, bE(height), hash)
  44. }
  45. func keyOutqueue(evidence types.Evidence, priority int64) []byte {
  46. return _key("%s/%s/%s/%X", baseKeyOutqueue, bE(priority), bE(evidence.Height()), evidence.Hash())
  47. }
  48. func keyPending(evidence types.Evidence) []byte {
  49. return _key("%s/%s/%X", baseKeyPending, bE(evidence.Height()), evidence.Hash())
  50. }
  51. func _key(fmt_ string, o ...interface{}) []byte {
  52. return []byte(fmt.Sprintf(fmt_, o...))
  53. }
  54. // EvidenceStore is a store of all the evidence we've seen, including
  55. // evidence that has been committed, evidence that has been verified but not broadcast,
  56. // and evidence that has been broadcast but not yet committed.
  57. type EvidenceStore struct {
  58. db dbm.DB
  59. }
  60. func NewEvidenceStore(db dbm.DB) *EvidenceStore {
  61. return &EvidenceStore{
  62. db: db,
  63. }
  64. }
  65. // PriorityEvidence returns the evidence from the outqueue, sorted by highest priority.
  66. func (store *EvidenceStore) PriorityEvidence() (evidence []types.Evidence) {
  67. // reverse the order so highest priority is first
  68. l := store.ListEvidence(baseKeyOutqueue)
  69. l2 := make([]types.Evidence, len(l))
  70. for i := range l {
  71. l2[i] = l[len(l)-1-i]
  72. }
  73. return l2
  74. }
  75. // PendingEvidence returns all known uncommitted evidence.
  76. func (store *EvidenceStore) PendingEvidence() (evidence []types.Evidence) {
  77. return store.ListEvidence(baseKeyPending)
  78. }
  79. // ListEvidence lists the evidence for the given prefix key.
  80. // It is wrapped by PriorityEvidence and PendingEvidence for convenience.
  81. func (store *EvidenceStore) ListEvidence(prefixKey string) (evidence []types.Evidence) {
  82. iter := dbm.IteratePrefix(store.db, []byte(prefixKey))
  83. for ; iter.Valid(); iter.Next() {
  84. val := iter.Value()
  85. var ei EvidenceInfo
  86. err := cdc.UnmarshalBinaryBare(val, &ei)
  87. if err != nil {
  88. panic(err)
  89. }
  90. evidence = append(evidence, ei.Evidence)
  91. }
  92. return evidence
  93. }
  94. // GetEvidence fetches the evidence with the given height and hash.
  95. func (store *EvidenceStore) GetEvidence(height int64, hash []byte) *EvidenceInfo {
  96. key := keyLookupFromHeightAndHash(height, hash)
  97. val := store.db.Get(key)
  98. if len(val) == 0 {
  99. return nil
  100. }
  101. var ei EvidenceInfo
  102. err := cdc.UnmarshalBinaryBare(val, &ei)
  103. if err != nil {
  104. panic(err)
  105. }
  106. return &ei
  107. }
  108. // AddNewEvidence adds the given evidence to the database.
  109. // It returns false if the evidence is already stored.
  110. func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int64) bool {
  111. // check if we already have seen it
  112. ei_ := store.GetEvidence(evidence.Height(), evidence.Hash())
  113. if ei_ != nil && ei_.Evidence != nil {
  114. return false
  115. }
  116. ei := EvidenceInfo{
  117. Committed: false,
  118. Priority: priority,
  119. Evidence: evidence,
  120. }
  121. eiBytes := cdc.MustMarshalBinaryBare(ei)
  122. // add it to the store
  123. key := keyOutqueue(evidence, priority)
  124. store.db.Set(key, eiBytes)
  125. key = keyPending(evidence)
  126. store.db.Set(key, eiBytes)
  127. key = keyLookup(evidence)
  128. store.db.SetSync(key, eiBytes)
  129. return true
  130. }
  131. // MarkEvidenceAsBroadcasted removes evidence from Outqueue.
  132. func (store *EvidenceStore) MarkEvidenceAsBroadcasted(evidence types.Evidence) {
  133. ei := store.getEvidenceInfo(evidence)
  134. key := keyOutqueue(evidence, ei.Priority)
  135. store.db.Delete(key)
  136. }
  137. // MarkEvidenceAsPending removes evidence from pending and outqueue and sets the state to committed.
  138. func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) {
  139. // if its committed, its been broadcast
  140. store.MarkEvidenceAsBroadcasted(evidence)
  141. pendingKey := keyPending(evidence)
  142. store.db.Delete(pendingKey)
  143. ei := store.getEvidenceInfo(evidence)
  144. ei.Committed = true
  145. lookupKey := keyLookup(evidence)
  146. store.db.SetSync(lookupKey, cdc.MustMarshalBinaryBare(ei))
  147. }
  148. //---------------------------------------------------
  149. // utils
  150. func (store *EvidenceStore) getEvidenceInfo(evidence types.Evidence) EvidenceInfo {
  151. key := keyLookup(evidence)
  152. var ei EvidenceInfo
  153. b := store.db.Get(key)
  154. err := cdc.UnmarshalBinaryBare(b, &ei)
  155. if err != nil {
  156. panic(err)
  157. }
  158. return ei
  159. }