From b5c3f8afb86a6def7749d568f09e0fbc24a7b8b7 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Fri, 18 Apr 2025 14:17:55 +0700 Subject: [PATCH 1/4] add CLI to improve genesis instead of update via script --- cmd/evmd/convert_address.go | 3 +- cmd/evmd/genesis/improve.go | 185 +++++++++++++++++++++++ cmd/evmd/genesis/root.go | 65 ++++++++ cmd/evmd/inspect/{inspect.go => root.go} | 0 cmd/evmd/root.go | 3 + local_node.sh | 67 ++------ 6 files changed, 270 insertions(+), 53 deletions(-) create mode 100644 cmd/evmd/genesis/improve.go create mode 100644 cmd/evmd/genesis/root.go rename cmd/evmd/inspect/{inspect.go => root.go} (100%) diff --git a/cmd/evmd/convert_address.go b/cmd/evmd/convert_address.go index de52786d3a..14ad7e0a89 100644 --- a/cmd/evmd/convert_address.go +++ b/cmd/evmd/convert_address.go @@ -17,7 +17,8 @@ func NewConvertAddressCmd() *cobra.Command { cmd := &cobra.Command{ Use: "convert-address [address] [?next-bech32]", Aliases: []string{"ca"}, - Short: fmt.Sprintf(`Convert account bech32 address into hex address or vice versa. + Short: "Convert account bech32 address into hex address or vice versa", + Long: fmt.Sprintf(`Convert account bech32 address into hex address or vice versa. If the second argument (next-bech32) is provided, it will convert the address into the input bech32 address. Eg: %s ca 0x830bb7a3c4c664e1c611d16dba1ef4067c697bd7 evmos diff --git a/cmd/evmd/genesis/improve.go b/cmd/evmd/genesis/improve.go new file mode 100644 index 0000000000..ca98f4945c --- /dev/null +++ b/cmd/evmd/genesis/improve.go @@ -0,0 +1,185 @@ +package genesis + +import ( + "encoding/json" + "fmt" + "math/big" + + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + + sdkmath "cosmossdk.io/math" + + cmttypes "github.com/cometbft/cometbft/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + + evmtypes "github.com/EscanBE/evermint/v12/x/evm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/EscanBE/evermint/v12/constants" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/spf13/cobra" +) + +func NewImproveGenesisCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "improve", + Short: "Improve genesis by update the genesis.json file with necessary changes", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return generalGenesisUpdateFunc(cmd, func(genesis map[string]json.RawMessage, clientCtx client.Context) error { + { // Update block max gas + consensusGenesis := &genutiltypes.ConsensusGenesis{} + err := consensusGenesis.UnmarshalJSON(genesis["consensus"]) + if err != nil { + return fmt.Errorf("failed to unmarshal consensus genesis: %w", err) + } + + if consensusGenesis.Params == nil { + consensusGenesis.Params = cmttypes.DefaultConsensusParams() + } + consensusGenesis.Params.Block.MaxGas = 36_000_000 + + // Marshal the updated consensus genesis back to genesis + updatedConsensusGenesis, err := consensusGenesis.MarshalJSON() + if err != nil { + return fmt.Errorf("failed to marshal updated consensus genesis: %w", err) + } + genesis["consensus"] = updatedConsensusGenesis + } + + { // Update the app state + var appState map[string]json.RawMessage + err := json.Unmarshal(genesis["app_state"], &appState) + if err != nil { + return fmt.Errorf("failed to unmarshal app state: %w", err) + } + + // Update genesis state for each module + appState["bank"] = improveGenesisOfBank(appState["bank"], clientCtx.Codec) + appState["staking"] = improveGenesisOfStaking(appState["staking"], clientCtx.Codec) + appState["mint"] = improveGenesisOfMint(appState["mint"], clientCtx.Codec) + appState["evm"] = improveGenesisOfEvm(appState["evm"], clientCtx.Codec) + appState["crisis"] = improveGenesisOfCrisis(appState["crisis"], clientCtx.Codec) + appState["gov"] = improveGenesisOfGov(appState["gov"], clientCtx.Codec) + appState["slashing"] = improveGenesisOfSlashing(appState["slashing"], clientCtx.Codec) + + // Marshal the updated app state back to genesis + updatedAppState, err := json.Marshal(appState) + if err != nil { + return fmt.Errorf("failed to marshal updated app state: %w", err) + } + genesis["app_state"] = updatedAppState + } + + return nil + }) + }, + } + + return cmd +} + +// improveGenesisOfBank adds denom metadata. +func improveGenesisOfBank(rawGenesisState json.RawMessage, codec codec.Codec) json.RawMessage { + var bankGenesisState banktypes.GenesisState + codec.MustUnmarshalJSON(rawGenesisState, &bankGenesisState) + + bankGenesisState.DenomMetadata = append(bankGenesisState.DenomMetadata, banktypes.Metadata{ + Description: "Native token of the chain", + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: constants.BaseDenom, + Exponent: 0, + }, + { + Denom: constants.SymbolDenom, + Exponent: constants.BaseDenomExponent, + }, + }, + Base: constants.BaseDenom, + Display: constants.SymbolDenom, + Name: constants.DisplayDenom, + Symbol: constants.SymbolDenom, + }) + + return codec.MustMarshalJSON(&bankGenesisState) +} + +// improveGenesisOfStaking updates bond denom. +func improveGenesisOfStaking(rawGenesisState json.RawMessage, codec codec.Codec) json.RawMessage { + var stakingGenesisState stakingtypes.GenesisState + codec.MustUnmarshalJSON(rawGenesisState, &stakingGenesisState) + + stakingGenesisState.Params.BondDenom = constants.BaseDenom + + return codec.MustMarshalJSON(&stakingGenesisState) +} + +// improveGenesisOfMint updates mint denom, goal bonded and inflation. +func improveGenesisOfMint(rawGenesisState json.RawMessage, codec codec.Codec) json.RawMessage { + var mintGenesisState minttypes.GenesisState + codec.MustUnmarshalJSON(rawGenesisState, &mintGenesisState) + + mintGenesisState.Params.MintDenom = constants.BaseDenom + mintGenesisState.Params.GoalBonded = sdkmath.LegacyNewDecWithPrec(50, 2) // 50% + mintGenesisState.Params.InflationMax = sdkmath.LegacyNewDecWithPrec(10, 2) // 10% + mintGenesisState.Params.InflationMin = sdkmath.LegacyNewDecWithPrec(3, 2) // 3% + + return codec.MustMarshalJSON(&mintGenesisState) +} + +// improveGenesisOfEvm updates evm denom. +func improveGenesisOfEvm(rawGenesisState json.RawMessage, codec codec.Codec) json.RawMessage { + var evmGenesisState evmtypes.GenesisState + codec.MustUnmarshalJSON(rawGenesisState, &evmGenesisState) + + evmGenesisState.Params.EvmDenom = constants.BaseDenom + + return codec.MustMarshalJSON(&evmGenesisState) +} + +// improveGenesisOfCrisis updates crisis denom and fee. +func improveGenesisOfCrisis(rawGenesisState json.RawMessage, codec codec.Codec) json.RawMessage { + var crisisGenesisState crisistypes.GenesisState + codec.MustUnmarshalJSON(rawGenesisState, &crisisGenesisState) + + crisisGenesisState.ConstantFee.Denom = constants.BaseDenom + crisisGenesisState.ConstantFee.Amount = sdkmath.NewIntFromBigInt(new(big.Int).Exp( + big.NewInt(10), big.NewInt(constants.BaseDenomExponent), nil, + )).MulRaw(10) + + return codec.MustMarshalJSON(&crisisGenesisState) +} + +// improveGenesisOfGov updates gov params like denom and deposit amount. +func improveGenesisOfGov(rawGenesisState json.RawMessage, codec codec.Codec) json.RawMessage { + var govGenesisState govtypesv1.GenesisState + codec.MustUnmarshalJSON(rawGenesisState, &govGenesisState) + + amountOfNative := func(amount int64) sdkmath.Int { + return sdkmath.NewIntFromBigInt(new(big.Int).Exp( + big.NewInt(10), big.NewInt(constants.BaseDenomExponent), nil, + )).MulRaw(amount) + } + + govGenesisState.Params.MinDeposit = sdk.NewCoins(sdk.NewCoin(constants.BaseDenom, amountOfNative(1_000))) + govGenesisState.Params.ExpeditedMinDeposit = sdk.NewCoins(sdk.NewCoin(constants.BaseDenom, amountOfNative(2_000))) + + return codec.MustMarshalJSON(&govGenesisState) +} + +// improveGenesisOfSlashing updates slashing params, increase the signed blocks window. +func improveGenesisOfSlashing(rawGenesisState json.RawMessage, codec codec.Codec) json.RawMessage { + var slashingGenesisState slashingtypes.GenesisState + codec.MustUnmarshalJSON(rawGenesisState, &slashingGenesisState) + + slashingGenesisState.Params.SignedBlocksWindow = 10_000 + + return codec.MustMarshalJSON(&slashingGenesisState) +} diff --git a/cmd/evmd/genesis/root.go b/cmd/evmd/genesis/root.go new file mode 100644 index 0000000000..de2cd9e1c5 --- /dev/null +++ b/cmd/evmd/genesis/root.go @@ -0,0 +1,65 @@ +package genesis + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" +) + +// Cmd creates a main CLI command +func Cmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "genesis", + Short: "Allow custom genesis", + } + + cmd.AddCommand( + NewImproveGenesisCmd(), + ) + + return cmd +} + +func generalGenesisUpdateFunc(cmd *cobra.Command, updater func(genesis map[string]json.RawMessage, clientCtx client.Context) error) error { + clientCtx := client.GetClientContextFromCmd(cmd) + if homeDir := clientCtx.HomeDir; homeDir == "" { + return fmt.Errorf("home dir not set") + } + + // Load the genesis file + genesisFile := fmt.Sprintf("%s/config/genesis.json", clientCtx.HomeDir) + genesisData, err := os.ReadFile(genesisFile) + if err != nil { + return fmt.Errorf("failed to read genesis file: %w", err) + } + + // Parse the genesis file + var genesis map[string]json.RawMessage + err = json.Unmarshal(genesisData, &genesis) + if err != nil { + return fmt.Errorf("failed to unmarshal genesis file: %w", err) + } + + // Update + err = updater(genesis, clientCtx) + if err != nil { + return fmt.Errorf("failed to update genesis: %w", err) + } + + // Marshal the updated genesis back to JSON + updatedGenesisData, err := json.MarshalIndent(genesis, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal updated genesis: %w", err) + } + + // Write the updated genesis back to the file + err = os.WriteFile(genesisFile, updatedGenesisData, 0o644) + if err != nil { + return fmt.Errorf("failed to write updated genesis file: %w", err) + } + + return nil +} diff --git a/cmd/evmd/inspect/inspect.go b/cmd/evmd/inspect/root.go similarity index 100% rename from cmd/evmd/inspect/inspect.go rename to cmd/evmd/inspect/root.go diff --git a/cmd/evmd/root.go b/cmd/evmd/root.go index ace43eadfe..e8ec948845 100644 --- a/cmd/evmd/root.go +++ b/cmd/evmd/root.go @@ -8,6 +8,8 @@ import ( "path/filepath" "time" + "github.com/EscanBE/evermint/v12/cmd/evmd/genesis" + "cosmossdk.io/client/v2/autocli" storetypes "cosmossdk.io/store/types" @@ -216,6 +218,7 @@ You gonna get "data/application.db" unpacked return snapshotCmd }(), inspect.Cmd(), + genesis.Cmd(), NewConvertAddressCmd(), } diff --git a/local_node.sh b/local_node.sh index 7b5f6dad44..63f1c7afa3 100755 --- a/local_node.sh +++ b/local_node.sh @@ -75,16 +75,8 @@ if [[ $overwrite == "y" || $overwrite == "Y" ]]; then # Set moniker for the node "$BINARY" init $MONIKER -o --chain-id $CHAINID --home "$HOMEDIR" - # Change parameter token denominations to native coin base denom - jq '.app_state["staking"]["params"]["bond_denom"]="'$MIN_DENOM'"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" - jq '.app_state["crisis"]["constant_fee"]["denom"]="'$MIN_DENOM'"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" - jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="'$MIN_DENOM'"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" # legacy gov - jq '.app_state["gov"]["params"]["min_deposit"][0]["denom"]="'$MIN_DENOM'"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" # v0.47 - jq '.app_state["evm"]["params"]["evm_denom"]="'$MIN_DENOM'"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" - jq '.app_state["mint"]["params"]["mint_denom"]="'$MIN_DENOM'"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" - - # Set gas limit in genesis - jq '.consensus_params["block"]["max_gas"]="10000000"' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" + # Improve genesis + "$BINARY" genesis improve --home "$HOMEDIR" # Set deployer authority DEPLOYER_ADDRESS=$("$BINARY" keys show "${KEYS[0]}" -a --keyring-backend $KEYRING --home "$HOMEDIR") @@ -93,43 +85,21 @@ if [[ $overwrite == "y" || $overwrite == "Y" ]]; then jq '.app_state["cpc"]["deploy_erc20_native"]=true' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" jq '.app_state["cpc"]["deploy_staking_contract"]=true' "$GENESIS" >"$TMP_GENESIS" && mv "$TMP_GENESIS" "$GENESIS" - if [[ $1 == "pending" ]]; then - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' 's/timeout_propose = "3s"/timeout_propose = "30s"/g' "$CONFIG" - sed -i '' 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' "$CONFIG" - sed -i '' 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' "$CONFIG" - sed -i '' 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' "$CONFIG" - sed -i '' 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' "$CONFIG" - sed -i '' 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' "$CONFIG" - sed -i '' 's/timeout_commit = "5s"/timeout_commit = "150s"/g' "$CONFIG" - sed -i '' 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' "$CONFIG" - else - sed -i 's/timeout_propose = "3s"/timeout_propose = "30s"/g' "$CONFIG" - sed -i 's/timeout_propose_delta = "500ms"/timeout_propose_delta = "5s"/g' "$CONFIG" - sed -i 's/timeout_prevote = "1s"/timeout_prevote = "10s"/g' "$CONFIG" - sed -i 's/timeout_prevote_delta = "500ms"/timeout_prevote_delta = "5s"/g' "$CONFIG" - sed -i 's/timeout_precommit = "1s"/timeout_precommit = "10s"/g' "$CONFIG" - sed -i 's/timeout_precommit_delta = "500ms"/timeout_precommit_delta = "5s"/g' "$CONFIG" - sed -i 's/timeout_commit = "5s"/timeout_commit = "150s"/g' "$CONFIG" - sed -i 's/timeout_broadcast_tx_commit = "10s"/timeout_broadcast_tx_commit = "150s"/g' "$CONFIG" - fi - fi - - # enable prometheus metrics - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' 's/prometheus = false/prometheus = true/' "$CONFIG" - sed -i '' 's/prometheus-retention-time = 0/prometheus-retention-time = 1000000000000/g' "$APP_TOML" - sed -i '' 's/enabled = false/enabled = true/g' "$APP_TOML" - else - sed -i 's/prometheus = false/prometheus = true/' "$CONFIG" - sed -i 's/prometheus-retention-time = "0"/prometheus-retention-time = "1000000000000"/g' "$APP_TOML" - sed -i 's/enabled = false/enabled = true/g' "$APP_TOML" - fi + # enable prometheus metrics + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' 's/prometheus = false/prometheus = true/' "$CONFIG" + sed -i '' 's/prometheus-retention-time = 0/prometheus-retention-time = 1000000000000/g' "$APP_TOML" + sed -i '' 's/enabled = false/enabled = true/g' "$APP_TOML" + else + sed -i 's/prometheus = false/prometheus = true/' "$CONFIG" + sed -i 's/prometheus-retention-time = "0"/prometheus-retention-time = "1000000000000"/g' "$APP_TOML" + sed -i 's/enabled = false/enabled = true/g' "$APP_TOML" + fi # Change proposal periods to pass within a reasonable time for local testing - sed -i.bak 's/"max_deposit_period": "172800s"/"max_deposit_period": "30s"/g' "$HOMEDIR"/config/genesis.json - sed -i.bak 's/"voting_period": "172800s"/"voting_period": "30s"/g' "$HOMEDIR"/config/genesis.json - sed -i.bak 's/"expedited_voting_period": "86400s"/"expedited_voting_period": "15s"/g' "$HOMEDIR"/config/genesis.json + sed -i.bak 's/"max_deposit_period": "172800s"/"max_deposit_period": "60s"/g' "$HOMEDIR"/config/genesis.json + sed -i.bak 's/"voting_period": "172800s"/"voting_period": "120s"/g' "$HOMEDIR"/config/genesis.json + sed -i.bak 's/"expedited_voting_period": "86400s"/"expedited_voting_period": "30s"/g' "$HOMEDIR"/config/genesis.json # set custom pruning settings sed -i.bak 's/pruning = "default"/pruning = "custom"/g' "$APP_TOML" @@ -162,15 +132,8 @@ if [[ $overwrite == "y" || $overwrite == "Y" ]]; then # Run this to ensure everything worked and that the genesis file is setup correctly "$BINARY" validate-genesis --home "$HOMEDIR" - - if [[ $1 == "pending" ]]; then - echo "pending mode is on, please wait for the first block committed." - fi fi -# Fix the initial height format -sed -i.bak 's/"initial_height": 1,/"initial_height": "1",/g' "$HOMEDIR"/config/genesis.json - # Start the node (remove the --pruning=nothing flag if historical queries are not needed) "$BINARY" start \ --metrics "$TRACE" --log_level "$LOGLEVEL" \ From 097090cb8fc3e3791729c024f9ad14d05c95bc51 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Fri, 18 Apr 2025 14:22:50 +0700 Subject: [PATCH 2/4] add CLI to add continuous/delayed and permanent locked vesting accounts to genesis --- cmd/evmd/genesis/add_vesting_account.go | 230 ++++++++++++++++++++++++ cmd/evmd/genesis/root.go | 1 + local_node.sh | 4 + 3 files changed, 235 insertions(+) create mode 100644 cmd/evmd/genesis/add_vesting_account.go diff --git a/cmd/evmd/genesis/add_vesting_account.go b/cmd/evmd/genesis/add_vesting_account.go new file mode 100644 index 0000000000..fe30f4306b --- /dev/null +++ b/cmd/evmd/genesis/add_vesting_account.go @@ -0,0 +1,230 @@ +package genesis + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/EscanBE/evermint/v12/constants" + "github.com/cosmos/cosmos-sdk/types/bech32" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + "github.com/ethereum/go-ethereum/common" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" +) + +func NewAddVestingAccountCmd() *cobra.Command { + const ( + flagContinuousVesting = "continuous-vesting" + flagDelayedVesting = "delayed-vesting" + flagPermanentLocked = "permanent-locked" + flagStartDate = "start-date" + flagEndDate = "end-date" + ) + + cmd := &cobra.Command{ + Use: "add-vesting-account [bech32/0xAddress] [amount]", + Short: "Add a continuous/delayed/permanent-locked vesting account.", + Long: `Add a continuous/delayed/permanent-locked vesting account. +The periodic vesting account type is not supported since it is complex in setting unlock schedule.`, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + var bech32Addr string + inputAddr := strings.ToLower(strings.TrimSpace(args[0])) + rawAmount := strings.TrimSpace(args[1]) + + if strings.HasPrefix(inputAddr, "0x") { + if !common.IsHexAddress(inputAddr) { + return fmt.Errorf("invalid 0x address: %s", inputAddr) + } + + bech32Addr = sdk.AccAddress(common.HexToAddress(inputAddr).Bytes()).String() + } else { + bech32Addr = inputAddr + } + + hrp, addrBz, err := bech32.DecodeAndConvert(bech32Addr) + if err != nil { + return fmt.Errorf("invalid bech32 address: %s", bech32Addr) + } else if hrp != constants.Bech32Prefix { + return fmt.Errorf("invalid bech32 prefix: %s", hrp) + } else if bzLen := len(addrBz); bzLen != 20 { + return fmt.Errorf("invalid bech32 address, require 20 bytes address but got %d", bzLen) + } + + coins, err := sdk.ParseCoinsNormalized(rawAmount) + if err != nil { + return fmt.Errorf("invalid amount %s: %w", rawAmount, err) + } else if !coins.IsAllPositive() { + return fmt.Errorf("invalid amount: %s", rawAmount) + } else if len(coins) != 1 || coins[0].Denom != constants.BaseDenom { + return fmt.Errorf("can only add only one coin of native: %s", rawAmount) + } + + sumBool := func(bs ...bool) int { + var sum int + for _, b := range bs { + if b { + sum++ + } + } + return sum + } + + continuousVesting := cmd.Flags().Changed(flagContinuousVesting) + delayedVesting := cmd.Flags().Changed(flagDelayedVesting) + permanentLocked := cmd.Flags().Changed(flagPermanentLocked) + if sumBool(continuousVesting, delayedVesting, permanentLocked) != 1 { + return fmt.Errorf("exactly one of --%s, --%s, or --%s must be specified", flagContinuousVesting, flagDelayedVesting, flagPermanentLocked) + } + + readDateFlag := func(flagName string) (time.Time, error) { + dateStr, err := cmd.Flags().GetString(flagName) + if err != nil { + return time.Time{}, err + } + + date, err := time.Parse(time.DateOnly, dateStr) + if err != nil { + return time.Time{}, fmt.Errorf("invalid date format for --%s: %w", flagName, err) + } + + return date.UTC(), nil + } + + return generalGenesisUpdateFunc(cmd, func(genesis map[string]json.RawMessage, clientCtx client.Context) error { + var appState map[string]json.RawMessage + { // Decode the app state + err := json.Unmarshal(genesis["app_state"], &appState) + if err != nil { + return fmt.Errorf("failed to unmarshal app state: %w", err) + } + + // Update bank genesis state + + } + + codec := clientCtx.Codec + + { // Add the vesting account to auth + var authGenesisState authtypes.GenesisState + codec.MustUnmarshalJSON(appState["auth"], &authGenesisState) + + accounts, err := authtypes.UnpackAccounts(authGenesisState.Accounts) + if err != nil { + return fmt.Errorf("failed to unpack genesis auth accounts: %w", err) + } + + var highestAccountNumber uint64 + for _, account := range accounts { + if bytes.Equal(account.GetAddress(), addrBz) { + return fmt.Errorf("account %s already exists as %s", bech32Addr, account.GetAddress()) + } + if account.GetAccountNumber() > highestAccountNumber { + highestAccountNumber = account.GetAccountNumber() + } + } + + baseAccount := authtypes.NewBaseAccount(addrBz, nil, highestAccountNumber+1, 0) + + var newVestingAccount authtypes.GenesisAccount + if continuousVesting { + startDate, err := readDateFlag("start-date") + if err != nil { + return fmt.Errorf("failed to read start date: %w", err) + } + endDate, err := readDateFlag("end-date") + if err != nil { + return fmt.Errorf("failed to read end date: %w", err) + } + if !endDate.After(startDate) { + return fmt.Errorf("end date by --%s must be after start date by --%s", flagEndDate, flagStartDate) + } + newVestingAccount, err = vestingtypes.NewContinuousVestingAccount(baseAccount, coins, startDate.Unix(), endDate.Unix()) + if err != nil { + return fmt.Errorf("failed to create continuous vesting account: %w", err) + } + } else if delayedVesting { + endDate, err := readDateFlag("end-date") + if err != nil { + return fmt.Errorf("failed to read end date: %w", err) + } + newVestingAccount, err = vestingtypes.NewDelayedVestingAccount(baseAccount, coins, endDate.Unix()) + if err != nil { + return fmt.Errorf("failed to create delayed vesting account: %w", err) + } + } else if permanentLocked { + newVestingAccount, err = vestingtypes.NewPermanentLockedAccount(baseAccount, coins) + if err != nil { + return fmt.Errorf("failed to create permanent locked account: %w", err) + } + } else { + return fmt.Errorf("unknown vesting type") + } + + authGenesisState.Accounts, err = authtypes.PackAccounts(append(accounts, newVestingAccount)) + if err != nil { + return fmt.Errorf("failed to pack genesis auth accounts: %w", err) + } + + appState["auth"] = codec.MustMarshalJSON(&authGenesisState) + } + + { // Add the vesting amount and total supply to bank + var bankGenesisState banktypes.GenesisState + codec.MustUnmarshalJSON(appState["bank"], &bankGenesisState) + + var foundExistingBalances bool + for i, balance := range bankGenesisState.Balances { + if balance.Address != bech32Addr { + continue + } + + // update to the existing balances + balance.Coins = balance.Coins.Add(coins...) + bankGenesisState.Balances[i] = balance + + foundExistingBalances = true + break + } + + if !foundExistingBalances { // if not found existing record, create new + bankGenesisState.Balances = append(bankGenesisState.Balances, banktypes.Balance{ + Address: bech32Addr, + Coins: coins, + }) + } + + bankGenesisState.Supply = bankGenesisState.Supply.Add(coins...) + + appState["bank"] = codec.MustMarshalJSON(&bankGenesisState) + } + + { // Marshal the updated app state back to genesis + updatedAppState, err := json.Marshal(appState) + if err != nil { + return fmt.Errorf("failed to marshal updated app state: %w", err) + } + genesis["app_state"] = updatedAppState + } + + return nil + }) + }, + } + + cmd.Flags().Bool(flagContinuousVesting, false, "Add a continuous vesting account") + cmd.Flags().Bool(flagDelayedVesting, false, "Add a delayed vesting account") + cmd.Flags().Bool(flagPermanentLocked, false, "Add a permanent locked account") + cmd.Flags().String(flagStartDate, time.Now().UTC().Format(time.DateOnly), "Start date for the continuous vesting account with format yyyy-mm-dd") + cmd.Flags().String(flagEndDate, time.Now().UTC().Add(365*24*time.Hour).Format(time.DateOnly), "End date for the continuous/delayed vesting account with format yyyy-mm-dd") + + return cmd +} diff --git a/cmd/evmd/genesis/root.go b/cmd/evmd/genesis/root.go index de2cd9e1c5..95d1457fef 100644 --- a/cmd/evmd/genesis/root.go +++ b/cmd/evmd/genesis/root.go @@ -18,6 +18,7 @@ func Cmd() *cobra.Command { cmd.AddCommand( NewImproveGenesisCmd(), + NewAddVestingAccountCmd(), ) return cmd diff --git a/local_node.sh b/local_node.sh index 63f1c7afa3..d0b9f0c501 100755 --- a/local_node.sh +++ b/local_node.sh @@ -112,6 +112,10 @@ if [[ $overwrite == "y" || $overwrite == "Y" ]]; then "$BINARY" add-genesis-account "${KEYS[1]}" "$GENESIS_BALANCE$MIN_DENOM" --keyring-backend $KEYRING --home "$HOMEDIR" "$BINARY" add-genesis-account "${KEYS[2]}" "$GENESIS_BALANCE$MIN_DENOM" --keyring-backend $KEYRING --home "$HOMEDIR" "$BINARY" add-genesis-account "${KEYS[3]}" "$GENESIS_BALANCE$MIN_DENOM" --keyring-backend $KEYRING --home "$HOMEDIR" + # Allocate some vesting accounts + # "$BINARY" genesis add-vesting-account "evm1...." "$GENESIS_BALANCE$MIN_DENOM" --home "$HOMEDIR" --continuous-vesting + # "$BINARY" genesis add-vesting-account "0x123..." "$GENESIS_BALANCE$MIN_DENOM" --home "$HOMEDIR" --delayed-vesting + # "$BINARY" genesis add-vesting-account "0x456..." "$GENESIS_BALANCE$MIN_DENOM" --home "$HOMEDIR" --permanent-locked # bc is required to add these big numbers total_supply=$(echo "${#KEYS[@]} * $GENESIS_BALANCE" | bc) From 6bb59eb0a4966f44073bb19c31251c6564064e2c Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Fri, 18 Apr 2025 14:25:21 +0700 Subject: [PATCH 3/4] correct genesis doc provider --- server/start.go | 2 +- server/util.go | 16 ++++++++++++++++ testutil/network/util.go | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/server/start.go b/server/start.go index 0880b709ba..9d0dfdd738 100644 --- a/server/start.go +++ b/server/start.go @@ -346,7 +346,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, opts StartOpt return err } - genDocProvider := cmtnode.DefaultGenesisDocProviderFunc(cfg) + genDocProvider := GenDocProvider(cfg) var ( cmtNode *cmtnode.Node diff --git a/server/util.go b/server/util.go index 554af32eba..4850caf71a 100644 --- a/server/util.go +++ b/server/util.go @@ -4,6 +4,10 @@ import ( "net" "time" + cmtcfg "github.com/cometbft/cometbft/config" + cmttypes "github.com/cometbft/cometbft/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + // TODO update import to local pkg when rpc pkg is migrated "github.com/EscanBE/evermint/v12/server/config" "github.com/spf13/cobra" @@ -99,3 +103,15 @@ func Listen(addr string, config *config.Config) (net.Listener, error) { } return ln, err } + +// GenDocProvider returns a function which returns the Comet genesis doc from the genesis file. +func GenDocProvider(cfg *cmtcfg.Config) func() (*cmttypes.GenesisDoc, error) { + return func() (*cmttypes.GenesisDoc, error) { + appGenesis, err := genutiltypes.AppGenesisFromFile(cfg.GenesisFile()) + if err != nil { + return nil, err + } + + return appGenesis.ToGenesisDoc() + } +} diff --git a/testutil/network/util.go b/testutil/network/util.go index 762797cd32..a64bbd8163 100644 --- a/testutil/network/util.go +++ b/testutil/network/util.go @@ -58,7 +58,7 @@ func startInProcess(cfg Config, val *Validator) error { app := cfg.AppConstructor(*val) - genDocProvider := cmtnode.DefaultGenesisDocProviderFunc(cometCfg) + genDocProvider := server.GenDocProvider(cometCfg) cmtApp := sdkserver.NewCometABCIWrapper(app) cometNode, err := cmtnode.NewNode( cometCfg, From fd86eb52d69dfc8bc678708672a259213ab7edf9 Mon Sep 17 00:00:00 2001 From: VictorTrustyDev Date: Fri, 18 Apr 2025 14:32:51 +0700 Subject: [PATCH 4/4] update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f03f294d08..395b723279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - (evm) [#175](https://github.com/EscanBE/evermint/pull/175) Support precompiled contract using new module `x/cpc` - (evm) [#178](https://github.com/EscanBE/evermint/pull/178) Staking precompiled contract ESIP-179 - (evm) [#182](https://github.com/EscanBE/evermint/pull/182) Bech32 precompiled contract ESIP-181 +- (cli) [#190](https://github.com/EscanBE/evermint/pull/190) Introduce `genesis` CLI for improve `genesis.json`, add genesis vesting account and minor bug fix ### Improvement