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.

268 lines
6.4 KiB

  1. package lite
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strconv"
  6. amino "github.com/tendermint/go-amino"
  7. cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
  8. dbm "github.com/tendermint/tendermint/libs/db"
  9. log "github.com/tendermint/tendermint/libs/log"
  10. lerr "github.com/tendermint/tendermint/lite/errors"
  11. "github.com/tendermint/tendermint/types"
  12. )
  13. type DBProvider struct {
  14. logger log.Logger
  15. label string
  16. db dbm.DB
  17. cdc *amino.Codec
  18. limit int
  19. }
  20. func NewDBProvider(label string, db dbm.DB) *DBProvider {
  21. // NOTE: when debugging, this type of construction might be useful.
  22. //db = dbm.NewDebugDB("db provider "+cmn.RandStr(4), db)
  23. cdc := amino.NewCodec()
  24. cryptoAmino.RegisterAmino(cdc)
  25. dbp := &DBProvider{
  26. logger: log.NewNopLogger(),
  27. label: label,
  28. db: db,
  29. cdc: cdc,
  30. }
  31. return dbp
  32. }
  33. func (dbp *DBProvider) SetLogger(logger log.Logger) {
  34. dbp.logger = logger.With("label", dbp.label)
  35. }
  36. func (dbp *DBProvider) SetLimit(limit int) *DBProvider {
  37. dbp.limit = limit
  38. return dbp
  39. }
  40. // Implements PersistentProvider.
  41. func (dbp *DBProvider) SaveFullCommit(fc FullCommit) error {
  42. dbp.logger.Info("DBProvider.SaveFullCommit()...", "fc", fc)
  43. batch := dbp.db.NewBatch()
  44. // Save the fc.validators.
  45. // We might be overwriting what we already have, but
  46. // it makes the logic easier for now.
  47. vsKey := validatorSetKey(fc.ChainID(), fc.Height())
  48. vsBz, err := dbp.cdc.MarshalBinary(fc.Validators)
  49. if err != nil {
  50. return err
  51. }
  52. batch.Set(vsKey, vsBz)
  53. // Save the fc.NextValidators.
  54. nvsKey := validatorSetKey(fc.ChainID(), fc.Height()+1)
  55. nvsBz, err := dbp.cdc.MarshalBinary(fc.NextValidators)
  56. if err != nil {
  57. return err
  58. }
  59. batch.Set(nvsKey, nvsBz)
  60. // Save the fc.SignedHeader
  61. shKey := signedHeaderKey(fc.ChainID(), fc.Height())
  62. shBz, err := dbp.cdc.MarshalBinary(fc.SignedHeader)
  63. if err != nil {
  64. return err
  65. }
  66. batch.Set(shKey, shBz)
  67. // And write sync.
  68. batch.WriteSync()
  69. // Garbage collect.
  70. // TODO: optimize later.
  71. if dbp.limit > 0 {
  72. dbp.deleteAfterN(fc.ChainID(), dbp.limit)
  73. }
  74. return nil
  75. }
  76. // Implements Provider.
  77. func (dbp *DBProvider) LatestFullCommit(chainID string, minHeight, maxHeight int64) (
  78. FullCommit, error) {
  79. dbp.logger.Info("DBProvider.LatestFullCommit()...",
  80. "chainID", chainID, "minHeight", minHeight, "maxHeight", maxHeight)
  81. if minHeight <= 0 {
  82. minHeight = 1
  83. }
  84. if maxHeight == 0 {
  85. maxHeight = 1<<63 - 1
  86. }
  87. itr := dbp.db.ReverseIterator(
  88. signedHeaderKey(chainID, maxHeight),
  89. signedHeaderKey(chainID, minHeight-1),
  90. )
  91. defer itr.Close()
  92. for itr.Valid() {
  93. key := itr.Key()
  94. _, _, ok := parseSignedHeaderKey(key)
  95. if !ok {
  96. // Skip over other keys.
  97. itr.Next()
  98. continue
  99. } else {
  100. // Found the latest full commit signed header.
  101. shBz := itr.Value()
  102. sh := types.SignedHeader{}
  103. err := dbp.cdc.UnmarshalBinary(shBz, &sh)
  104. if err != nil {
  105. return FullCommit{}, err
  106. } else {
  107. lfc, err := dbp.fillFullCommit(sh)
  108. if err == nil {
  109. dbp.logger.Info("DBProvider.LatestFullCommit() found latest.", "height", lfc.Height())
  110. return lfc, nil
  111. } else {
  112. dbp.logger.Error("DBProvider.LatestFullCommit() got error", "lfc", lfc)
  113. dbp.logger.Error(fmt.Sprintf("%+v", err))
  114. return lfc, err
  115. }
  116. }
  117. }
  118. }
  119. return FullCommit{}, lerr.ErrCommitNotFound()
  120. }
  121. func (dbp *DBProvider) ValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
  122. return dbp.getValidatorSet(chainID, height)
  123. }
  124. func (dbp *DBProvider) getValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
  125. vsBz := dbp.db.Get(validatorSetKey(chainID, height))
  126. if vsBz == nil {
  127. err = lerr.ErrUnknownValidators(chainID, height)
  128. return
  129. }
  130. err = dbp.cdc.UnmarshalBinary(vsBz, &valset)
  131. if err != nil {
  132. return
  133. }
  134. // To test deep equality. This makes it easier to test for e.g. valset
  135. // equivalence using assert.Equal (tests for deep equality) in our tests,
  136. // which also tests for unexported/private field equivalence.
  137. valset.TotalVotingPower()
  138. return
  139. }
  140. func (dbp *DBProvider) fillFullCommit(sh types.SignedHeader) (FullCommit, error) {
  141. var chainID = sh.ChainID
  142. var height = sh.Height
  143. var valset, nextValset *types.ValidatorSet
  144. // Load the validator set.
  145. valset, err := dbp.getValidatorSet(chainID, height)
  146. if err != nil {
  147. return FullCommit{}, err
  148. }
  149. // Load the next validator set.
  150. nextValset, err = dbp.getValidatorSet(chainID, height+1)
  151. if err != nil {
  152. return FullCommit{}, err
  153. }
  154. // Return filled FullCommit.
  155. return FullCommit{
  156. SignedHeader: sh,
  157. Validators: valset,
  158. NextValidators: nextValset,
  159. }, nil
  160. }
  161. func (dbp *DBProvider) deleteAfterN(chainID string, after int) error {
  162. dbp.logger.Info("DBProvider.deleteAfterN()...", "chainID", chainID, "after", after)
  163. itr := dbp.db.ReverseIterator(
  164. signedHeaderKey(chainID, 1<<63-1),
  165. signedHeaderKey(chainID, 0),
  166. )
  167. defer itr.Close()
  168. var lastHeight int64 = 1<<63 - 1
  169. var numSeen = 0
  170. var numDeleted = 0
  171. for itr.Valid() {
  172. key := itr.Key()
  173. _, height, ok := parseChainKeyPrefix(key)
  174. if !ok {
  175. return fmt.Errorf("unexpected key %v", key)
  176. } else {
  177. if height < lastHeight {
  178. lastHeight = height
  179. numSeen += 1
  180. }
  181. if numSeen > after {
  182. dbp.db.Delete(key)
  183. numDeleted += 1
  184. }
  185. }
  186. itr.Next()
  187. }
  188. dbp.logger.Info(fmt.Sprintf("DBProvider.deleteAfterN() deleted %v items", numDeleted))
  189. return nil
  190. }
  191. //----------------------------------------
  192. // key encoding
  193. func signedHeaderKey(chainID string, height int64) []byte {
  194. return []byte(fmt.Sprintf("%s/%010d/sh", chainID, height))
  195. }
  196. func validatorSetKey(chainID string, height int64) []byte {
  197. return []byte(fmt.Sprintf("%s/%010d/vs", chainID, height))
  198. }
  199. //----------------------------------------
  200. // key parsing
  201. var keyPattern = regexp.MustCompile(`^([^/]+)/([0-9]*)/(.*)$`)
  202. func parseKey(key []byte) (chainID string, height int64, part string, ok bool) {
  203. submatch := keyPattern.FindSubmatch(key)
  204. if submatch == nil {
  205. return "", 0, "", false
  206. }
  207. chainID = string(submatch[1])
  208. heightStr := string(submatch[2])
  209. heightInt, err := strconv.Atoi(heightStr)
  210. if err != nil {
  211. return "", 0, "", false
  212. }
  213. height = int64(heightInt)
  214. part = string(submatch[3])
  215. ok = true // good!
  216. return
  217. }
  218. func parseSignedHeaderKey(key []byte) (chainID string, height int64, ok bool) {
  219. chainID, height, part, ok := parseKey(key)
  220. if part != "sh" {
  221. return "", 0, false
  222. }
  223. return chainID, height, true
  224. }
  225. func parseChainKeyPrefix(key []byte) (chainID string, height int64, ok bool) {
  226. chainID, height, _, ok = parseKey(key)
  227. return chainID, height, true
  228. }