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.

168 lines
4.8 KiB

  1. package types
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "time"
  9. "github.com/tendermint/tendermint/crypto"
  10. "github.com/tendermint/tendermint/internal/jsontypes"
  11. tmbytes "github.com/tendermint/tendermint/libs/bytes"
  12. tmtime "github.com/tendermint/tendermint/libs/time"
  13. )
  14. const (
  15. // MaxChainIDLen is a maximum length of the chain ID.
  16. MaxChainIDLen = 50
  17. )
  18. //------------------------------------------------------------
  19. // core types for a genesis definition
  20. // NOTE: any changes to the genesis definition should
  21. // be reflected in the documentation:
  22. // docs/tendermint-core/using-tendermint.md
  23. // GenesisValidator is an initial validator.
  24. type GenesisValidator struct {
  25. Address Address
  26. PubKey crypto.PubKey
  27. Power int64
  28. Name string
  29. }
  30. type genesisValidatorJSON struct {
  31. Address Address `json:"address"`
  32. PubKey json.RawMessage `json:"pub_key"`
  33. Power int64 `json:"power,string"`
  34. Name string `json:"name"`
  35. }
  36. func (g GenesisValidator) MarshalJSON() ([]byte, error) {
  37. pk, err := jsontypes.Marshal(g.PubKey)
  38. if err != nil {
  39. return nil, err
  40. }
  41. return json.Marshal(genesisValidatorJSON{
  42. Address: g.Address, PubKey: pk, Power: g.Power, Name: g.Name,
  43. })
  44. }
  45. func (g *GenesisValidator) UnmarshalJSON(data []byte) error {
  46. var gv genesisValidatorJSON
  47. if err := json.Unmarshal(data, &gv); err != nil {
  48. return err
  49. }
  50. if err := jsontypes.Unmarshal(gv.PubKey, &g.PubKey); err != nil {
  51. return err
  52. }
  53. g.Address = gv.Address
  54. g.Power = gv.Power
  55. g.Name = gv.Name
  56. return nil
  57. }
  58. // GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set.
  59. type GenesisDoc struct {
  60. GenesisTime time.Time `json:"genesis_time"`
  61. ChainID string `json:"chain_id"`
  62. InitialHeight int64 `json:"initial_height,string"`
  63. ConsensusParams *ConsensusParams `json:"consensus_params,omitempty"`
  64. Validators []GenesisValidator `json:"validators,omitempty"`
  65. AppHash tmbytes.HexBytes `json:"app_hash"`
  66. AppState json.RawMessage `json:"app_state,omitempty"`
  67. }
  68. // SaveAs is a utility method for saving GenensisDoc as a JSON file.
  69. func (genDoc *GenesisDoc) SaveAs(file string) error {
  70. genDocBytes, err := json.MarshalIndent(genDoc, "", " ")
  71. if err != nil {
  72. return err
  73. }
  74. return os.WriteFile(file, genDocBytes, 0644) // nolint:gosec
  75. }
  76. // ValidatorHash returns the hash of the validator set contained in the GenesisDoc
  77. func (genDoc *GenesisDoc) ValidatorHash() []byte {
  78. vals := make([]*Validator, len(genDoc.Validators))
  79. for i, v := range genDoc.Validators {
  80. vals[i] = NewValidator(v.PubKey, v.Power)
  81. }
  82. vset := NewValidatorSet(vals)
  83. return vset.Hash()
  84. }
  85. // ValidateAndComplete checks that all necessary fields are present
  86. // and fills in defaults for optional fields left empty
  87. func (genDoc *GenesisDoc) ValidateAndComplete() error {
  88. if genDoc.ChainID == "" {
  89. return errors.New("genesis doc must include non-empty chain_id")
  90. }
  91. if len(genDoc.ChainID) > MaxChainIDLen {
  92. return fmt.Errorf("chain_id in genesis doc is too long (max: %d)", MaxChainIDLen)
  93. }
  94. if genDoc.InitialHeight < 0 {
  95. return fmt.Errorf("initial_height cannot be negative (got %v)", genDoc.InitialHeight)
  96. }
  97. if genDoc.InitialHeight == 0 {
  98. genDoc.InitialHeight = 1
  99. }
  100. if genDoc.ConsensusParams == nil {
  101. genDoc.ConsensusParams = DefaultConsensusParams()
  102. } else if err := genDoc.ConsensusParams.ValidateConsensusParams(); err != nil {
  103. return err
  104. }
  105. for i, v := range genDoc.Validators {
  106. if v.Power == 0 {
  107. return fmt.Errorf("the genesis file cannot contain validators with no voting power: %v", v)
  108. }
  109. if len(v.Address) > 0 && !bytes.Equal(v.PubKey.Address(), v.Address) {
  110. return fmt.Errorf("incorrect address for validator %v in the genesis file, should be %v", v, v.PubKey.Address())
  111. }
  112. if len(v.Address) == 0 {
  113. genDoc.Validators[i].Address = v.PubKey.Address()
  114. }
  115. }
  116. if genDoc.GenesisTime.IsZero() {
  117. genDoc.GenesisTime = tmtime.Now()
  118. }
  119. return nil
  120. }
  121. //------------------------------------------------------------
  122. // Make genesis state from file
  123. // GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc.
  124. func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) {
  125. genDoc := GenesisDoc{}
  126. err := json.Unmarshal(jsonBlob, &genDoc)
  127. if err != nil {
  128. return nil, err
  129. }
  130. if err := genDoc.ValidateAndComplete(); err != nil {
  131. return nil, err
  132. }
  133. return &genDoc, err
  134. }
  135. // GenesisDocFromFile reads JSON data from a file and unmarshalls it into a GenesisDoc.
  136. func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) {
  137. jsonBlob, err := os.ReadFile(genDocFile)
  138. if err != nil {
  139. return nil, fmt.Errorf("couldn't read GenesisDoc file: %w", err)
  140. }
  141. genDoc, err := GenesisDocFromJSON(jsonBlob)
  142. if err != nil {
  143. return nil, fmt.Errorf("error reading GenesisDoc at %s: %w", genDocFile, err)
  144. }
  145. return genDoc, nil
  146. }