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.

264 lines
6.3 KiB

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