github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/types/genesis.go (about) 1 package types 2 3 import ( 4 "fmt" 5 "os" 6 "time" 7 8 "github.com/gnolang/gno/tm2/pkg/amino" 9 abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" 10 tmtime "github.com/gnolang/gno/tm2/pkg/bft/types/time" 11 "github.com/gnolang/gno/tm2/pkg/crypto" 12 "github.com/gnolang/gno/tm2/pkg/errors" 13 osm "github.com/gnolang/gno/tm2/pkg/os" 14 ) 15 16 const ( 17 // MaxChainIDLen is a maximum length of the chain ID. 18 MaxChainIDLen = 50 19 ) 20 21 var ( 22 ErrEmptyChainID = errors.New("chain ID is empty") 23 ErrLongChainID = fmt.Errorf("chain ID cannot be longer than %d chars", MaxChainIDLen) 24 ErrInvalidGenesisTime = errors.New("invalid genesis time") 25 ErrNoValidators = errors.New("no validators in set") 26 ErrInvalidValidatorVotingPower = errors.New("validator has no voting power") 27 ErrInvalidValidatorAddress = errors.New("invalid validator address") 28 ErrValidatorPubKeyMismatch = errors.New("validator public key and address mismatch") 29 ) 30 31 // ------------------------------------------------------------ 32 // core types for a genesis definition 33 // NOTE: any changes to the genesis definition should 34 // be reflected in the documentation: 35 // docs/tendermint-core/using-tendermint.md 36 37 // GenesisValidator is an initial validator. 38 type GenesisValidator struct { 39 Address Address `json:"address"` 40 PubKey crypto.PubKey `json:"pub_key"` 41 Power int64 `json:"power"` 42 Name string `json:"name"` 43 } 44 45 // GenesisDoc defines the initial conditions for a tendermint blockchain, in particular its validator set. 46 type GenesisDoc struct { 47 GenesisTime time.Time `json:"genesis_time"` 48 ChainID string `json:"chain_id"` 49 ConsensusParams abci.ConsensusParams `json:"consensus_params,omitempty"` 50 Validators []GenesisValidator `json:"validators,omitempty"` 51 AppHash []byte `json:"app_hash"` 52 AppState interface{} `json:"app_state,omitempty"` 53 } 54 55 // SaveAs is a utility method for saving GenensisDoc as a JSON file. 56 func (genDoc *GenesisDoc) SaveAs(file string) error { 57 genDocBytes, err := amino.MarshalJSONIndent(genDoc, "", " ") 58 if err != nil { 59 return err 60 } 61 return osm.WriteFile(file, genDocBytes, 0o644) 62 } 63 64 // ValidatorHash returns the hash of the validator set contained in the GenesisDoc 65 func (genDoc *GenesisDoc) ValidatorHash() []byte { 66 vals := make([]*Validator, len(genDoc.Validators)) 67 for i, v := range genDoc.Validators { 68 vals[i] = NewValidator(v.PubKey, v.Power) 69 } 70 vset := NewValidatorSet(vals) 71 return vset.Hash() 72 } 73 74 // Validate validates the genesis doc 75 func (genDoc *GenesisDoc) Validate() error { 76 // Make sure the chain ID is not empty 77 if genDoc.ChainID == "" { 78 return ErrEmptyChainID 79 } 80 81 // Make sure the chain ID is < max chain ID length 82 if len(genDoc.ChainID) > MaxChainIDLen { 83 return ErrLongChainID 84 } 85 86 // Make sure the genesis time is valid 87 if genDoc.GenesisTime.IsZero() { 88 return ErrInvalidGenesisTime 89 } 90 91 // Validate the consensus params 92 if consensusParamsErr := ValidateConsensusParams(genDoc.ConsensusParams); consensusParamsErr != nil { 93 return consensusParamsErr 94 } 95 96 // Make sure there are validators in the set 97 if len(genDoc.Validators) == 0 { 98 return ErrNoValidators 99 } 100 101 // Make sure the validators are valid 102 for _, v := range genDoc.Validators { 103 // Check the voting power 104 if v.Power == 0 { 105 return fmt.Errorf("%w, %s", ErrInvalidValidatorVotingPower, v.Name) 106 } 107 108 // Check the address 109 if v.Address.IsZero() { 110 return fmt.Errorf("%w, %s", ErrInvalidValidatorAddress, v.Name) 111 } 112 113 // Check the pub key -> address matching 114 if v.PubKey.Address() != v.Address { 115 return fmt.Errorf("%w, %s", ErrValidatorPubKeyMismatch, v.Name) 116 } 117 } 118 119 return nil 120 } 121 122 // ValidateAndComplete checks that all necessary fields are present 123 // and fills in defaults for optional fields left empty 124 func (genDoc *GenesisDoc) ValidateAndComplete() error { 125 if genDoc.ChainID == "" { 126 return errors.New("Genesis doc must include non-empty chain_id") 127 } 128 if len(genDoc.ChainID) > MaxChainIDLen { 129 return errors.New("chain_id in genesis doc is too long (max: %d)", MaxChainIDLen) 130 } 131 132 // Start from defaults and fill in consensus params from GenesisDoc. 133 genDoc.ConsensusParams = DefaultConsensusParams().Update(genDoc.ConsensusParams) 134 if err := ValidateConsensusParams(genDoc.ConsensusParams); err != nil { 135 return err 136 } 137 138 for i, v := range genDoc.Validators { 139 if v.Power == 0 { 140 return errors.New("The genesis file cannot contain validators with no voting power: %v", v) 141 } 142 if v.Address.IsZero() { 143 genDoc.Validators[i].Address = v.PubKey.Address() 144 } else if v.PubKey.Address() != v.Address { 145 return errors.New("Incorrect address for validator %v in the genesis file, should be %v", v, v.PubKey.Address()) 146 } 147 } 148 149 if genDoc.GenesisTime.IsZero() { 150 genDoc.GenesisTime = tmtime.Now() 151 } 152 153 return nil 154 } 155 156 // ------------------------------------------------------------ 157 // Make genesis state from file 158 159 // GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc. 160 func GenesisDocFromJSON(jsonBlob []byte) (*GenesisDoc, error) { 161 genDoc := GenesisDoc{} 162 err := amino.UnmarshalJSON(jsonBlob, &genDoc) 163 if err != nil { 164 return nil, err 165 } 166 167 if err := genDoc.ValidateAndComplete(); err != nil { 168 return nil, err 169 } 170 171 return &genDoc, err 172 } 173 174 // GenesisDocFromFile reads JSON data from a file and unmarshalls it into a GenesisDoc. 175 func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) { 176 jsonBlob, err := os.ReadFile(genDocFile) 177 if err != nil { 178 return nil, errors.Wrap(err, "Couldn't read GenesisDoc file") 179 } 180 genDoc, err := GenesisDocFromJSON(jsonBlob) 181 if err != nil { 182 return nil, errors.Wrap(err, fmt.Sprintf("Error reading GenesisDoc at %v", genDocFile)) 183 } 184 return genDoc, nil 185 } 186 187 // ---------------------------------------- 188 // Mock AppState (for testing) 189 190 type MockAppState struct { 191 AccountOwner string `json:"account_owner"` 192 }