gcx/blockchain/genex-chain/cmd/genexd/cmd/root.go

381 lines
12 KiB
Go

// Package cmd — Genex Chain CLI 命令
package cmd
import (
"errors"
"io"
"os"
"github.com/cosmos/evm/x/vm/types"
"github.com/spf13/cast"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmtcfg "github.com/cometbft/cometbft/config"
cmtcli "github.com/cometbft/cometbft/libs/cli"
dbm "github.com/cosmos/cosmos-db"
cosmosevmcmd "github.com/cosmos/evm/client"
evmdebug "github.com/cosmos/evm/client/debug"
evmconfig "github.com/cosmos/evm/config"
"github.com/cosmos/evm/crypto/hd"
cosmosevmserver "github.com/cosmos/evm/server"
srvflags "github.com/cosmos/evm/server/flags"
"cosmossdk.io/log"
"cosmossdk.io/store"
snapshottypes "cosmossdk.io/store/snapshots/types"
storetypes "cosmossdk.io/store/types"
confixcmd "cosmossdk.io/tools/confix/cmd"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
clientcfg "github.com/cosmos/cosmos-sdk/client/config"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/pruning"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/snapshot"
sdkserver "github.com/cosmos/cosmos-sdk/server"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
genexchain "github.com/genex/genex-chain"
)
const (
genexEVMChainID = 8888
)
// NewRootCmd creates the root command for genexd
func NewRootCmd() *cobra.Command {
// Pre-instantiate the application for encoding config
tempApp := genexchain.NewGenexApp(
log.NewNopLogger(),
dbm.NewMemDB(),
nil,
true,
simtestutil.EmptyAppOptions{},
)
encodingConfig := sdktestutil.TestEncodingConfig{
InterfaceRegistry: tempApp.InterfaceRegistry(),
Codec: tempApp.AppCodec(),
TxConfig: tempApp.GetTxConfig(),
Amino: tempApp.LegacyAmino(),
}
initClientCtx := client.Context{}.
WithCodec(encodingConfig.Codec).
WithInterfaceRegistry(encodingConfig.InterfaceRegistry).
WithTxConfig(encodingConfig.TxConfig).
WithLegacyAmino(encodingConfig.Amino).
WithInput(os.Stdin).
WithAccountRetriever(authtypes.AccountRetriever{}).
WithBroadcastMode(flags.FlagBroadcastMode).
WithHomeDir(genexchain.GenexDefaultNodeHome()).
WithViper("").
// Cosmos EVM specific setup
WithKeyringOptions(hd.EthSecp256k1Option()).
WithLedgerHasProtobuf(true)
rootCmd := &cobra.Command{
Use: "genexd",
Short: "Genex Chain — 券金融专用应用链",
Long: `Genex Chain 是基于 Cosmos SDK + cosmos/evm + CometBFT 构建的券金融专用应用链。
特性:
- 完全 EVM 兼容 (Solidity, Hardhat, MetaMask)
- CometBFT 即时终结性共识 (≤1s 出块)
- 链级合规 (OFAC, Travel Rule, KYC)
- 平台 Gas 全额补贴 (用户零 Gas)
- IBC 跨链 + ERC-20 代币桥接
EVM Chain ID: 8888
Bech32 Prefix: genex
Bond Denom: agnx (GNX)`,
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
cmd.SetOut(cmd.OutOrStdout())
cmd.SetErr(cmd.ErrOrStderr())
initClientCtx = initClientCtx.WithCmdContext(cmd.Context())
initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags())
if err != nil {
return err
}
initClientCtx, err = clientcfg.ReadFromClientConfig(initClientCtx)
if err != nil {
return err
}
// Enable SIGN_MODE_TEXTUAL when online
if !initClientCtx.Offline {
enabledSignModes := append(tx.DefaultSignModes, signing.SignMode_SIGN_MODE_TEXTUAL)
txConfigOpts := tx.ConfigOptions{
EnabledSignModes: enabledSignModes,
TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx),
}
txConfig, err := tx.NewTxConfigWithOptions(initClientCtx.Codec, txConfigOpts)
if err != nil {
return err
}
initClientCtx = initClientCtx.WithTxConfig(txConfig)
}
if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil {
return err
}
// Genex Chain config
customAppTemplate, customAppConfig := evmconfig.InitAppConfig(
types.DefaultEVMExtendedDenom,
genexEVMChainID,
)
customTMConfig := initCometConfig()
return sdkserver.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customTMConfig)
},
}
initRootCmd(rootCmd, tempApp)
autoCliOpts := tempApp.AutoCliOpts()
initClientCtx, _ = clientcfg.ReadFromClientConfig(initClientCtx)
autoCliOpts.ClientCtx = initClientCtx
if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
panic(err)
}
return rootCmd
}
// initCometConfig returns CometBFT config optimized for Genex Chain
func initCometConfig() *cmtcfg.Config {
cfg := cmtcfg.DefaultConfig()
// Genex Chain: 1s timeout_commit → 实际出块 ~0.75s (单验证者)
// 多节点部署时 (20-50 验证者): ~1.5-2s
// 对标: dYdX 1-2s, Injective 1.2s, Cosmos Hub 6-7s
cfg.Consensus.TimeoutCommit = 1_000_000_000 // 1s
// 防止空块风暴: 无交易时每 60s 出一个心跳块
cfg.Consensus.CreateEmptyBlocks = false
cfg.Consensus.CreateEmptyBlocksInterval = 60_000_000_000 // 60s
return cfg
}
func initRootCmd(rootCmd *cobra.Command, genexApp *genexchain.GenexApp) {
cfg := sdk.GetConfig()
cfg.Seal()
defaultNodeHome := genexchain.GenexDefaultNodeHome()
sdkAppCreator := func(l log.Logger, d dbm.DB, w io.Writer, ao servertypes.AppOptions) servertypes.Application {
return newApp(l, d, w, ao)
}
rootCmd.AddCommand(
genutilcli.InitCmd(genexApp.BasicModuleManager, defaultNodeHome),
genutilcli.Commands(genexApp.TxConfig(), genexApp.BasicModuleManager, defaultNodeHome),
cmtcli.NewCompletionCmd(rootCmd, true),
evmdebug.Cmd(),
confixcmd.ConfigCommand(),
pruning.Cmd(sdkAppCreator, defaultNodeHome),
snapshot.Cmd(sdkAppCreator),
)
// Add cosmos/evm server commands (start, tendermint, etc.)
cosmosevmserver.AddCommands(
rootCmd,
cosmosevmserver.NewDefaultStartOptions(newApp, defaultNodeHome),
appExport,
addModuleInitFlags,
)
// Add cosmos/evm key commands (with EthSecp256k1 support)
rootCmd.AddCommand(
cosmosevmcmd.KeyCommands(defaultNodeHome, true),
)
// Add status, query, tx commands
rootCmd.AddCommand(
sdkserver.StatusCommand(),
queryCommand(),
txCommand(),
)
// Add general tx flags
var err error
_, err = srvflags.AddTxFlags(rootCmd)
if err != nil {
panic(err)
}
}
func addModuleInitFlags(_ *cobra.Command) {}
func queryCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "query",
Aliases: []string{"q"},
Short: "Querying subcommands",
DisableFlagParsing: false,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
cmd.AddCommand(
rpc.QueryEventForTxCmd(),
rpc.ValidatorCommand(),
authcmd.QueryTxsByEventsCmd(),
authcmd.QueryTxCmd(),
sdkserver.QueryBlockCmd(),
sdkserver.QueryBlockResultsCmd(),
)
cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID")
return cmd
}
func txCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "tx",
Short: "Transactions subcommands",
DisableFlagParsing: false,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
cmd.AddCommand(
authcmd.GetSignCommand(),
authcmd.GetSignBatchCommand(),
authcmd.GetMultiSignCommand(),
authcmd.GetMultiSignBatchCmd(),
authcmd.GetValidateSignaturesCommand(),
authcmd.GetBroadcastCommand(),
authcmd.GetEncodeCommand(),
authcmd.GetDecodeCommand(),
authcmd.GetSimulateCmd(),
)
cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID")
return cmd
}
// newApp creates the GenexApp with all configuration
func newApp(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
appOpts servertypes.AppOptions,
) cosmosevmserver.Application {
var cache storetypes.MultiStorePersistentCache
if cast.ToBool(appOpts.Get(sdkserver.FlagInterBlockCache)) {
cache = store.NewCommitKVStoreCacheManager()
}
pruningOpts, err := sdkserver.GetPruningOptionsFromFlags(appOpts)
if err != nil {
panic(err)
}
chainID, err := getChainIDFromOpts(appOpts)
if err != nil {
panic(err)
}
snapshotStore, err := sdkserver.GetSnapshotStore(appOpts)
if err != nil {
panic(err)
}
snapshotOptions := snapshottypes.NewSnapshotOptions(
cast.ToUint64(appOpts.Get(sdkserver.FlagStateSyncSnapshotInterval)),
cast.ToUint32(appOpts.Get(sdkserver.FlagStateSyncSnapshotKeepRecent)),
)
baseappOptions := []func(*baseapp.BaseApp){
baseapp.SetPruning(pruningOpts),
baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(sdkserver.FlagMinGasPrices))),
baseapp.SetQueryGasLimit(cast.ToUint64(appOpts.Get(sdkserver.FlagQueryGasLimit))),
baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(sdkserver.FlagHaltHeight))),
baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(sdkserver.FlagHaltTime))),
baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(sdkserver.FlagMinRetainBlocks))),
baseapp.SetInterBlockCache(cache),
baseapp.SetTrace(cast.ToBool(appOpts.Get(sdkserver.FlagTrace))),
baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(sdkserver.FlagIndexEvents))),
baseapp.SetSnapshot(snapshotStore, snapshotOptions),
baseapp.SetIAVLCacheSize(cast.ToInt(appOpts.Get(sdkserver.FlagIAVLCacheSize))),
baseapp.SetIAVLDisableFastNode(cast.ToBool(appOpts.Get(sdkserver.FlagDisableIAVLFastNode))),
baseapp.SetChainID(chainID),
// Optimistic Execution: 在 ProcessProposal 阶段预执行下一区块,
// 当 FinalizeBlock 调用时直接使用预执行结果,减少出块延迟。
// SDK v0.53+ 内置,利用 ABCI 2.0 并行处理能力。
baseapp.SetOptimisticExecution(),
}
return genexchain.NewGenexApp(
logger, db, traceStore, true,
appOpts,
baseappOptions...,
)
}
// appExport exports state for genesis
func appExport(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
height int64,
forZeroHeight bool,
jailAllowedAddrs []string,
appOpts servertypes.AppOptions,
modulesToExport []string,
) (servertypes.ExportedApp, error) {
homePath, ok := appOpts.Get(flags.FlagHome).(string)
if !ok || homePath == "" {
return servertypes.ExportedApp{}, errors.New("application home not set")
}
viperAppOpts, ok := appOpts.(*viper.Viper)
if !ok {
return servertypes.ExportedApp{}, errors.New("appOpts is not viper.Viper")
}
viperAppOpts.Set(sdkserver.FlagInvCheckPeriod, 1)
appOpts = viperAppOpts
chainID, err := getChainIDFromOpts(appOpts)
if err != nil {
return servertypes.ExportedApp{}, err
}
var genexApp *genexchain.GenexApp
if height != -1 {
genexApp = genexchain.NewGenexApp(logger, db, traceStore, false, appOpts, baseapp.SetChainID(chainID))
if err := genexApp.LoadHeight(height); err != nil {
return servertypes.ExportedApp{}, err
}
} else {
genexApp = genexchain.NewGenexApp(logger, db, traceStore, true, appOpts, baseapp.SetChainID(chainID))
}
return genexApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
}
// getChainIDFromOpts returns the chain ID from app options
func getChainIDFromOpts(appOpts servertypes.AppOptions) (string, error) {
chainID := cast.ToString(appOpts.Get(flags.FlagChainID))
if chainID == "" {
homeDir := cast.ToString(appOpts.Get(flags.FlagHome))
var err error
chainID, err = evmconfig.GetChainIDFromHome(homeDir)
if err != nil {
return "", err
}
}
return chainID, nil
}