381 lines
12 KiB
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
|
|
}
|