Some checks are pending
Docs Deploy / build_and_deploy (push) Waiting to run
Generate Docs / cli (push) Waiting to run
Generate Config Doc / cli (push) Waiting to run
Go formatting / go-formatting (push) Waiting to run
Check links / markdown-link-check (push) Waiting to run
Integration / pre-test (push) Waiting to run
Integration / test on (push) Blocked by required conditions
Integration / status (push) Blocked by required conditions
Lint / Lint Go code (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
333 lines
8.5 KiB
Go
333 lines
8.5 KiB
Go
package chain
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"dario.cat/mergo"
|
|
|
|
chainconfig "github.com/ignite/cli/v29/ignite/config/chain"
|
|
chaincmdrunner "github.com/ignite/cli/v29/ignite/pkg/chaincmd/runner"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cliui/view/accountview"
|
|
"github.com/ignite/cli/v29/ignite/pkg/confile"
|
|
"github.com/ignite/cli/v29/ignite/pkg/events"
|
|
)
|
|
|
|
type (
|
|
// InitArgs represents argument to add additional initialization for the chain.
|
|
// InitAccounts initializes chain accounts from the Ignite config.
|
|
// InitConfiguration initializes node configuration from the Ignite config.
|
|
// InitGenesis initializes genesis state for the chain from Ignite config.
|
|
InitArgs struct {
|
|
InitAccounts bool
|
|
InitConfiguration bool
|
|
InitGenesis bool
|
|
}
|
|
)
|
|
|
|
const (
|
|
moniker = "mynode"
|
|
)
|
|
|
|
var (
|
|
// InitArgsAll performs all initialization for the chain.
|
|
InitArgsAll = InitArgs{
|
|
InitAccounts: true,
|
|
InitConfiguration: true,
|
|
InitGenesis: true,
|
|
}
|
|
|
|
// InitArgsNone performs minimal initialization for the chain by only initializing a node.
|
|
InitArgsNone = InitArgs{
|
|
InitAccounts: false,
|
|
InitConfiguration: false,
|
|
InitGenesis: false,
|
|
}
|
|
)
|
|
|
|
// Init initializes the chain and accounts.
|
|
func (c *Chain) Init(ctx context.Context, args InitArgs) error {
|
|
if err := c.InitChain(ctx, args.InitConfiguration, args.InitGenesis); err != nil {
|
|
return err
|
|
}
|
|
|
|
if args.InitAccounts {
|
|
conf, err := c.Config()
|
|
if err != nil {
|
|
return &CannotBuildAppError{err}
|
|
}
|
|
|
|
return c.InitAccounts(ctx, conf)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// InitChain initializes the chain.
|
|
func (c *Chain) InitChain(ctx context.Context, initConfiguration, initGenesis bool) error {
|
|
chainID, err := c.ID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// cleanup persistent data from previous `serve`.
|
|
home, err := c.Home()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.RemoveAll(home); err != nil {
|
|
return err
|
|
}
|
|
|
|
var conf *chainconfig.Config
|
|
if initConfiguration || initGenesis {
|
|
conf, err = c.Config()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
commands, err := c.Commands(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// init node.
|
|
var initOptions []string
|
|
if denom := conf.DefaultDenom; len(denom) > 0 {
|
|
initOptions = append(initOptions,
|
|
fmt.Sprintf("--default-denom=%s", denom),
|
|
)
|
|
}
|
|
|
|
if err := commands.Init(ctx, moniker, initOptions...); err != nil {
|
|
return err
|
|
}
|
|
|
|
// overwrite app config files with the values defined in Ignite's config file
|
|
if initConfiguration {
|
|
if err := c.Configure(home, chainID, conf); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if initGenesis {
|
|
// make sure that chain id given during chain.New() has the most priority.
|
|
if conf.Genesis != nil {
|
|
conf.Genesis["chain_id"] = chainID
|
|
}
|
|
|
|
// update genesis file with the genesis values defined in the config
|
|
if err := c.UpdateGenesisFile(conf.Genesis); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// InitAccounts initializes the chain accounts and creates validator gentxs.
|
|
func (c *Chain) InitAccounts(ctx context.Context, cfg *chainconfig.Config) error {
|
|
commands, err := c.Commands(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.ev.Send("Initializing accounts...", events.ProgressUpdate())
|
|
|
|
var accounts accountview.Accounts
|
|
|
|
// add accounts from config into genesis
|
|
for _, account := range cfg.Accounts {
|
|
var generatedAccount chaincmdrunner.Account
|
|
accountAddress := account.Address
|
|
|
|
// If the account doesn't provide an address, we create one
|
|
if accountAddress == "" {
|
|
generatedAccount, err = commands.AddAccount(
|
|
ctx,
|
|
account.Name,
|
|
account.Mnemonic,
|
|
account.CoinType,
|
|
account.AccountNumber,
|
|
account.AddressIndex,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
accountAddress = generatedAccount.Address
|
|
}
|
|
|
|
coins := strings.Join(account.Coins, ",")
|
|
if err := commands.AddGenesisAccount(ctx, accountAddress, coins); err != nil {
|
|
return err
|
|
}
|
|
|
|
if account.Address == "" {
|
|
accounts = accounts.Append(accountview.NewAccount(
|
|
generatedAccount.Name,
|
|
accountAddress,
|
|
accountview.WithMnemonic(generatedAccount.Mnemonic),
|
|
))
|
|
} else {
|
|
accounts = accounts.Append(accountview.NewAccount(account.Name, accountAddress))
|
|
}
|
|
}
|
|
|
|
c.ev.SendView(accounts, events.ProgressFinish())
|
|
|
|
// 0 length validator set when using network config
|
|
if len(cfg.Validators) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if cfg.IsConsumerChain() {
|
|
// we skip early if the chain is a consumer chain
|
|
return nil
|
|
}
|
|
|
|
// Sovereign chain writes validators in gentxs.
|
|
if _, err := c.IssueGentx(ctx, createValidatorFromConfig(cfg)); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IssueGentx generates a gentx from the validator information in chain config and imports it in the chain genesis.
|
|
func (c Chain) IssueGentx(ctx context.Context, v Validator) (string, error) {
|
|
commands, err := c.Commands(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// create the gentx from the validator from the config
|
|
gentxPath, err := c.Gentx(ctx, commands, v)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// import the gentx into the genesis
|
|
return gentxPath, commands.CollectGentxs(ctx)
|
|
}
|
|
|
|
// IsInitialized checks if the chain is initialized.
|
|
// The check is performed by checking if the gentx dir exists in the config,
|
|
// unless c is a consumer chain, in which case the check relies on checking if
|
|
// the consumer genesis module is filled with validators.
|
|
func (c *Chain) IsInitialized() (bool, error) {
|
|
home, err := c.Home()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
cfg, err := c.Config()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if cfg.IsConsumerChain() {
|
|
// when consumer chain, we skip the IsInialized logic
|
|
return true, nil
|
|
}
|
|
|
|
gentxDir := filepath.Join(home, "config", "gentx")
|
|
|
|
if _, err := os.Stat(gentxDir); os.IsNotExist(err) {
|
|
return false, nil
|
|
} else if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// UpdateGenesisFile updates the chain genesis with a generic map of data.
|
|
// Updates are made using an override merge strategy.
|
|
func (c Chain) UpdateGenesisFile(data map[string]interface{}) error {
|
|
path, err := c.GenesisPath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
genesis := make(map[string]interface{})
|
|
cf := confile.New(confile.DefaultJSONEncodingCreator, path)
|
|
if err := cf.Load(&genesis); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := mergo.Merge(&genesis, data, mergo.WithOverride); err != nil {
|
|
return err
|
|
}
|
|
|
|
return cf.Save(genesis)
|
|
}
|
|
|
|
type Validator struct {
|
|
Name string
|
|
Moniker string
|
|
StakingAmount string
|
|
CommissionRate string
|
|
CommissionMaxRate string
|
|
CommissionMaxChangeRate string
|
|
MinSelfDelegation string
|
|
GasPrices string
|
|
Details string
|
|
Identity string
|
|
Website string
|
|
SecurityContact string
|
|
}
|
|
|
|
// Account represents an account in the chain.
|
|
type Account struct {
|
|
Name string
|
|
Address string
|
|
Mnemonic string `json:"mnemonic"`
|
|
CoinType string
|
|
Coins string
|
|
}
|
|
|
|
func createValidatorFromConfig(conf *chainconfig.Config) (validator Validator) {
|
|
// Currently, we support the config file with one valid validator.
|
|
validatorFromConfig := conf.Validators[0]
|
|
validator.Name = validatorFromConfig.Name
|
|
validator.StakingAmount = validatorFromConfig.Bonded
|
|
|
|
if validatorFromConfig.Gentx != nil {
|
|
if validatorFromConfig.Gentx.Amount != "" {
|
|
validator.StakingAmount = validatorFromConfig.Gentx.Amount
|
|
}
|
|
if validatorFromConfig.Gentx.Moniker != "" {
|
|
validator.Moniker = validatorFromConfig.Gentx.Moniker
|
|
}
|
|
if validatorFromConfig.Gentx.CommissionRate != "" {
|
|
validator.CommissionRate = validatorFromConfig.Gentx.CommissionRate
|
|
}
|
|
if validatorFromConfig.Gentx.CommissionMaxRate != "" {
|
|
validator.CommissionMaxRate = validatorFromConfig.Gentx.CommissionMaxRate
|
|
}
|
|
if validatorFromConfig.Gentx.CommissionMaxChangeRate != "" {
|
|
validator.CommissionMaxChangeRate = validatorFromConfig.Gentx.CommissionMaxChangeRate
|
|
}
|
|
if validatorFromConfig.Gentx.GasPrices != "" {
|
|
validator.GasPrices = validatorFromConfig.Gentx.GasPrices
|
|
}
|
|
if validatorFromConfig.Gentx.Details != "" {
|
|
validator.Details = validatorFromConfig.Gentx.Details
|
|
}
|
|
if validatorFromConfig.Gentx.Identity != "" {
|
|
validator.Identity = validatorFromConfig.Gentx.Identity
|
|
}
|
|
if validatorFromConfig.Gentx.Website != "" {
|
|
validator.Website = validatorFromConfig.Gentx.Website
|
|
}
|
|
if validatorFromConfig.Gentx.SecurityContact != "" {
|
|
validator.SecurityContact = validatorFromConfig.Gentx.SecurityContact
|
|
}
|
|
if validatorFromConfig.Gentx.MinSelfDelegation != "" {
|
|
validator.MinSelfDelegation = validatorFromConfig.Gentx.MinSelfDelegation
|
|
}
|
|
}
|
|
return validator
|
|
}
|