github.com/Finschia/ostracon@v1.1.5/types/genesis.go (about)

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