d068f5b331
* Problem: verbose logs display with FATAL option (fix #320) add my script increase amount for metamask add run amount ok hide log show info my logger hook log revive eth log tidy up use suplog log replace ok removed suplog tidy up tidy up fix compile remove sh tidy up tidy up * logger handler * fix * fix eth log override (#371) remove redundant log tidy up * log test * c++ Co-authored-by: jongwhan lee <jonghwan@crypto.com> Co-authored-by: Jongwhan Lee <51560997+leejw51crypto@users.noreply.github.com>
279 lines
7.7 KiB
Go
279 lines
7.7 KiB
Go
package network
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
jsonrpc "github.com/ethereum/go-ethereum/rpc"
|
|
"github.com/gorilla/mux"
|
|
"github.com/improbable-eng/grpc-web/go/grpcweb"
|
|
"github.com/rs/cors"
|
|
|
|
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"
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
"github.com/cosmos/cosmos-sdk/x/genutil"
|
|
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
|
|
"github.com/tharsis/ethermint/ethereum/rpc"
|
|
ethsrv "github.com/tharsis/ethermint/server"
|
|
ethermint "github.com/tharsis/ethermint/types"
|
|
)
|
|
|
|
func startInProcess(cfg Config, val *Validator) error {
|
|
logger := val.Ctx.Logger
|
|
tmCfg := val.Ctx.Config
|
|
tmCfg.Instrumentation.Prometheus = false
|
|
|
|
nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile())
|
|
if err != nil {
|
|
return fmt.Errorf("failed to load or generate node key: %w", 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 fmt.Errorf("failed to create node: %w", err)
|
|
}
|
|
|
|
if err := tmNode.Start(); err != nil {
|
|
return fmt.Errorf("failed to start node: %w", 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.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 fmt.Errorf("failed to start API server: %w", err)
|
|
case <-time.After(5 * time.Second): // assume server started successfully
|
|
}
|
|
|
|
val.api = apiSrv
|
|
}
|
|
|
|
if val.AppConfig.GRPC.Enable {
|
|
grpcSrv, err := servergrpc.StartGRPCServer(val.ClientCtx, app, val.AppConfig.GRPC.Address)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to start gRPC server: %w", err)
|
|
}
|
|
|
|
val.grpc = grpcSrv
|
|
}
|
|
|
|
if val.AppConfig.JSONRPC.Enable {
|
|
tmEndpoint := "/websocket"
|
|
tmRPCAddr := val.Ctx.Config.RPC.ListenAddress
|
|
tmWsClient := ethsrv.ConnectTmWS(tmRPCAddr, tmEndpoint, val.Ctx.Logger)
|
|
|
|
val.jsonRPC = jsonrpc.NewServer()
|
|
|
|
rpcAPIArr := val.AppConfig.JSONRPC.API
|
|
apis := rpc.GetRPCAPIs(val.Ctx, val.ClientCtx, tmWsClient, rpcAPIArr)
|
|
|
|
for _, api := range apis {
|
|
if err := val.jsonRPC.RegisterName(api.Namespace, api.Service); err != nil {
|
|
return fmt.Errorf("failed to register JSON-RPC namespace %s: %w", api.Namespace, err)
|
|
}
|
|
}
|
|
|
|
r := mux.NewRouter()
|
|
r.HandleFunc("/", val.jsonRPC.ServeHTTP).Methods("POST")
|
|
if val.grpc != nil {
|
|
grpcWeb := grpcweb.WrapServer(val.grpc)
|
|
ethsrv.MountGRPCWebServices(r, grpcWeb, grpcweb.ListGRPCResources(val.grpc), val.Ctx.Logger)
|
|
}
|
|
|
|
handlerWithCors := cors.New(cors.Options{
|
|
AllowedOrigins: []string{"*"},
|
|
AllowedMethods: []string{
|
|
http.MethodHead,
|
|
http.MethodGet,
|
|
http.MethodPost,
|
|
http.MethodPut,
|
|
http.MethodPatch,
|
|
http.MethodDelete,
|
|
},
|
|
AllowedHeaders: []string{"*"},
|
|
AllowCredentials: false,
|
|
OptionsPassthrough: false,
|
|
})
|
|
|
|
httpSrv := &http.Server{
|
|
Addr: strings.TrimPrefix(val.AppConfig.JSONRPC.Address, "tcp://"), // FIXME: timeouts
|
|
// Addr: val.AppConfig.JSONRPC.RPCAddress, // FIXME: address has too many colons
|
|
Handler: handlerWithCors.Handler(r),
|
|
}
|
|
|
|
httpSrvDone := make(chan struct{}, 1)
|
|
|
|
errCh := make(chan error)
|
|
go func() {
|
|
if err := httpSrv.ListenAndServe(); err != nil {
|
|
if err == http.ErrServerClosed {
|
|
close(httpSrvDone)
|
|
return
|
|
}
|
|
|
|
errCh <- err
|
|
}
|
|
}()
|
|
|
|
select {
|
|
case err := <-errCh:
|
|
return fmt.Errorf("JSON-RPC go routine failed to start: %w", err)
|
|
case <-time.After(1 * time.Second): // assume EVM RPC server started successfully
|
|
}
|
|
}
|
|
|
|
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, "ethermintd")
|
|
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 fmt.Errorf("failed to create genesis doc: %w", err)
|
|
}
|
|
|
|
appState, err := genutil.GenAppStateFromConfig(cfg.Codec, cfg.TxConfig,
|
|
tmCfg, initCfg, *genDoc, banktypes.GenesisBalancesIterator{})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create app state: %w", 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 fmt.Errorf("failed to export genesis: %w", 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 = 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 = ethermint.AttoPhoton
|
|
cfg.GenesisState[stakingtypes.ModuleName] = cfg.Codec.MustMarshalJSON(&stakingGenState)
|
|
|
|
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 {
|
|
writePath := filepath.Join(dir)
|
|
file := filepath.Join(writePath, name)
|
|
|
|
err := tmos.EnsureDir(writePath, 0755)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return tmos.WriteFile(file, contents, 0644)
|
|
}
|