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.

134 lines
3.4 KiB

  1. /*
  2. Package files defines a Provider that stores all data in the filesystem
  3. We assume the same validator hash may be reused by many different
  4. headers/Commits, and thus store it separately. This leaves us
  5. with three issues:
  6. 1. Given a validator hash, retrieve the validator set if previously stored
  7. 2. Given a block height, find the Commit with the highest height <= h
  8. 3. Given a FullCommit, store it quickly to satisfy 1 and 2
  9. Note that we do not worry about caching, as that can be achieved by
  10. pairing this with a MemStoreProvider and CacheProvider from certifiers
  11. */
  12. package files
  13. import (
  14. "encoding/hex"
  15. "fmt"
  16. "math"
  17. "os"
  18. "path/filepath"
  19. "sort"
  20. "github.com/pkg/errors"
  21. "github.com/tendermint/tendermint/certifiers"
  22. certerr "github.com/tendermint/tendermint/certifiers/errors"
  23. )
  24. const (
  25. Ext = ".tsd"
  26. ValDir = "validators"
  27. CheckDir = "checkpoints"
  28. dirPerm = os.FileMode(0755)
  29. filePerm = os.FileMode(0644)
  30. )
  31. type provider struct {
  32. valDir string
  33. checkDir string
  34. }
  35. // NewProvider creates the parent dir and subdirs
  36. // for validators and checkpoints as needed
  37. func NewProvider(dir string) certifiers.Provider {
  38. valDir := filepath.Join(dir, ValDir)
  39. checkDir := filepath.Join(dir, CheckDir)
  40. for _, d := range []string{valDir, checkDir} {
  41. err := os.MkdirAll(d, dirPerm)
  42. if err != nil {
  43. panic(err)
  44. }
  45. }
  46. return &provider{valDir: valDir, checkDir: checkDir}
  47. }
  48. func (p *provider) encodeHash(hash []byte) string {
  49. return hex.EncodeToString(hash) + Ext
  50. }
  51. func (p *provider) encodeHeight(h int) string {
  52. // pad up to 10^12 for height...
  53. return fmt.Sprintf("%012d%s", h, Ext)
  54. }
  55. func (p *provider) StoreCommit(fc certifiers.FullCommit) error {
  56. // make sure the fc is self-consistent before saving
  57. err := fc.ValidateBasic(fc.Commit.Header.ChainID)
  58. if err != nil {
  59. return err
  60. }
  61. paths := []string{
  62. filepath.Join(p.checkDir, p.encodeHeight(fc.Height())),
  63. filepath.Join(p.valDir, p.encodeHash(fc.Header.ValidatorsHash)),
  64. }
  65. for _, path := range paths {
  66. err := SaveFullCommit(fc, path)
  67. // unknown error in creating or writing immediately breaks
  68. if err != nil {
  69. return err
  70. }
  71. }
  72. return nil
  73. }
  74. func (p *provider) GetByHeight(h int) (certifiers.FullCommit, error) {
  75. // first we look for exact match, then search...
  76. path := filepath.Join(p.checkDir, p.encodeHeight(h))
  77. fc, err := LoadFullCommit(path)
  78. if certerr.IsCommitNotFoundErr(err) {
  79. path, err = p.searchForHeight(h)
  80. if err == nil {
  81. fc, err = LoadFullCommit(path)
  82. }
  83. }
  84. return fc, err
  85. }
  86. func (p *provider) LatestCommit() (fc certifiers.FullCommit, err error) {
  87. // Note to future: please update by 2077 to avoid rollover
  88. return p.GetByHeight(math.MaxInt32 - 1)
  89. }
  90. // search for height, looks for a file with highest height < h
  91. // return certifiers.ErrCommitNotFound() if not there...
  92. func (p *provider) searchForHeight(h int) (string, error) {
  93. d, err := os.Open(p.checkDir)
  94. if err != nil {
  95. return "", errors.WithStack(err)
  96. }
  97. files, err := d.Readdirnames(0)
  98. d.Close()
  99. if err != nil {
  100. return "", errors.WithStack(err)
  101. }
  102. desired := p.encodeHeight(h)
  103. sort.Strings(files)
  104. i := sort.SearchStrings(files, desired)
  105. if i == 0 {
  106. return "", certerr.ErrCommitNotFound()
  107. }
  108. found := files[i-1]
  109. path := filepath.Join(p.checkDir, found)
  110. return path, errors.WithStack(err)
  111. }
  112. func (p *provider) GetByHash(hash []byte) (certifiers.FullCommit, error) {
  113. path := filepath.Join(p.valDir, p.encodeHash(hash))
  114. return LoadFullCommit(path)
  115. }