// Copyright 2021 Evmos Foundation // This file is part of Evmos' Ethermint library. // // The Ethermint library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The Ethermint library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE package network import ( "encoding/json" "fmt" "path/filepath" "time" "github.com/ethereum/go-ethereum/ethclient" tmos "github.com/tendermint/tendermint/libs/os" "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/rpc/client/local" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" "github.com/cosmos/cosmos-sdk/server/api" servergrpc "github.com/cosmos/cosmos-sdk/server/grpc" srvtypes "github.com/cosmos/cosmos-sdk/server/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" mintypes "github.com/cosmos/cosmos-sdk/x/mint/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/evmos/ethermint/server" evmtypes "github.com/evmos/ethermint/x/evm/types" ) func startInProcess(cfg Config, val *Validator) error { logger := val.Ctx.Logger tmCfg := val.Ctx.Config tmCfg.Instrumentation.Prometheus = false if err := val.AppConfig.ValidateBasic(); err != nil { return err } nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) if err != nil { return err } app := cfg.AppConstructor(*val) genDocProvider := node.DefaultGenesisDocProviderFunc(tmCfg) tmNode, err := node.NewNode( tmCfg, pvm.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()), nodeKey, proxy.NewLocalClientCreator(app), genDocProvider, node.DefaultDBProvider, node.DefaultMetricsProvider(tmCfg.Instrumentation), logger.With("module", val.Moniker), ) if err != nil { return err } if err := tmNode.Start(); err != nil { return err } val.tmNode = tmNode if val.RPCAddress != "" { val.RPCClient = local.New(tmNode) } // We'll need a RPC client if the validator exposes a gRPC or REST endpoint. if val.APIAddress != "" || val.AppConfig.GRPC.Enable { val.ClientCtx = val.ClientCtx. WithClient(val.RPCClient) // Add the tx service in the gRPC router. app.RegisterTxService(val.ClientCtx) // Add the tendermint queries service in the gRPC router. app.RegisterTendermintService(val.ClientCtx) } if val.AppConfig.API.Enable && val.APIAddress != "" { apiSrv := api.New(val.ClientCtx, logger.With("module", "api-server")) app.RegisterAPIRoutes(apiSrv, val.AppConfig.API) errCh := make(chan error) go func() { if err := apiSrv.Start(val.AppConfig.Config); err != nil { errCh <- err } }() select { case err := <-errCh: return err case <-time.After(srvtypes.ServerStartTime): // assume server started successfully } val.api = apiSrv } if val.AppConfig.GRPC.Enable { grpcSrv, err := servergrpc.StartGRPCServer(val.ClientCtx, app, val.AppConfig.GRPC) if err != nil { return err } val.grpc = grpcSrv if val.AppConfig.GRPCWeb.Enable { val.grpcWeb, err = servergrpc.StartGRPCWeb(grpcSrv, val.AppConfig.Config) if err != nil { return err } } } if val.AppConfig.JSONRPC.Enable && val.AppConfig.JSONRPC.Address != "" { if val.Ctx == nil || val.Ctx.Viper == nil { return fmt.Errorf("validator %s context is nil", val.Moniker) } tmEndpoint := "/websocket" tmRPCAddr := val.RPCAddress val.jsonrpc, val.jsonrpcDone, err = server.StartJSONRPC(val.Ctx, val.ClientCtx, tmRPCAddr, tmEndpoint, val.AppConfig, nil) if err != nil { return err } address := fmt.Sprintf("http://%s", val.AppConfig.JSONRPC.Address) val.JSONRPCClient, err = ethclient.Dial(address) if err != nil { return fmt.Errorf("failed to dial JSON-RPC at %s: %w", val.AppConfig.JSONRPC.Address, err) } } return nil } func collectGenFiles(cfg Config, vals []*Validator, outputDir string) error { genTime := tmtime.Now() for i := 0; i < cfg.NumValidators; i++ { tmCfg := vals[i].Ctx.Config nodeDir := filepath.Join(outputDir, vals[i].Moniker, "evmosd") gentxsDir := filepath.Join(outputDir, "gentxs") tmCfg.Moniker = vals[i].Moniker tmCfg.SetRoot(nodeDir) initCfg := genutiltypes.NewInitConfig(cfg.ChainID, gentxsDir, vals[i].NodeID, vals[i].PubKey) genFile := tmCfg.GenesisFile() genDoc, err := types.GenesisDocFromFile(genFile) if err != nil { return err } appState, err := genutil.GenAppStateFromConfig(cfg.Codec, cfg.TxConfig, tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{}) if err != nil { return err } // overwrite each validator's genesis file to have a canonical genesis time if err := genutil.ExportGenesisFileWithTime(genFile, cfg.ChainID, nil, appState, genTime); err != nil { return err } } return nil } func initGenFiles(cfg Config, genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, genFiles []string) error { // set the accounts in the genesis state var authGenState authtypes.GenesisState cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[authtypes.ModuleName], &authGenState) accounts, err := authtypes.PackAccounts(genAccounts) if err != nil { return err } authGenState.Accounts = append(authGenState.Accounts, accounts...) cfg.GenesisState[authtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&authGenState) // set the balances in the genesis state var bankGenState banktypes.GenesisState cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[banktypes.ModuleName], &bankGenState) bankGenState.Balances = genBalances cfg.GenesisState[banktypes.ModuleName] = cfg.Codec.MustMarshalJSON(&bankGenState) var stakingGenState stakingtypes.GenesisState cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[stakingtypes.ModuleName], &stakingGenState) stakingGenState.Params.BondDenom = cfg.BondDenom cfg.GenesisState[stakingtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&stakingGenState) var govGenState govv1.GenesisState cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[govtypes.ModuleName], &govGenState) govGenState.DepositParams.MinDeposit[0].Denom = cfg.BondDenom cfg.GenesisState[govtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&govGenState) var mintGenState mintypes.GenesisState cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[mintypes.ModuleName], &mintGenState) mintGenState.Params.MintDenom = cfg.BondDenom cfg.GenesisState[mintypes.ModuleName] = cfg.Codec.MustMarshalJSON(&mintGenState) var crisisGenState crisistypes.GenesisState cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[crisistypes.ModuleName], &crisisGenState) crisisGenState.ConstantFee.Denom = cfg.BondDenom cfg.GenesisState[crisistypes.ModuleName] = cfg.Codec.MustMarshalJSON(&crisisGenState) var evmGenState evmtypes.GenesisState cfg.Codec.MustUnmarshalJSON(cfg.GenesisState[evmtypes.ModuleName], &evmGenState) evmGenState.Params.EvmDenom = cfg.BondDenom cfg.GenesisState[evmtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&evmGenState) appGenStateJSON, err := json.MarshalIndent(cfg.GenesisState, "", " ") if err != nil { return err } genDoc := types.GenesisDoc{ ChainID: cfg.ChainID, AppState: appGenStateJSON, Validators: nil, } // generate empty genesis files for each validator and save for i := 0; i < cfg.NumValidators; i++ { if err := genDoc.SaveAs(genFiles[i]); err != nil { return err } } return nil } func WriteFile(name string, dir string, contents []byte) error { file := filepath.Join(dir, name) err := tmos.EnsureDir(dir, 0o755) if err != nil { return err } return tmos.WriteFile(file, contents, 0o644) }