diff --git a/Makefile b/Makefile index 67911b299..3b0a523b6 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,8 @@ DOCKER := $(shell which docker) DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf:1.0.0-rc8 BUILDDIR ?= $(CURDIR)/build HTTPS_GIT := https://github.com/notional-labs/composable-centauri.git +TESTNET_NVAL := 6 +TESTNET_CHAINID := test-1 export GO111MODULE = on @@ -160,3 +162,47 @@ ictest-push-wasm: cd tests/interchaintest && go test -race -v -run TestPushWasmClientCode . .PHONY: ictest-start-cosmos ictest-start-polkadot ictest-ibc ictest-push-wasm ictest-all + +############################################################################### +### Localnet ### +############################################################################### + +build-linux: + mkdir -p $(BUILDDIR) + docker build --platform linux/amd64 --tag centaurid ./ + docker create --platform linux/amd64 --name temp centaurid:latest + docker cp temp:/bin/centaurid $(BUILDDIR)/ + docker rm temp + +localnet-start: localnet-stop + @if ! [ -f build/node0/$(BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/centauri:Z centaurid testnet init-files --chain-id ${TESTNET_CHAINID} --v ${TESTNET_NVAL} -o /centauri --keyring-backend=test --starting-ip-address 192.168.0.2; fi + +localnet-stop: + docker-compose down + rm -rf build/node* + rm -rf build/gentxs. + +############################################################################### +### Upgrade ### +############################################################################### +build-cosmovisor-linux: + @if [ -z "$(docker images -q centauri/centauri.cosmovisor-binary 2> /dev/null)" ]; then \ + $(MAKE) -C contrib/updates build-cosmovisor-linux BUILDDIR=$(BUILDDIR); \ + fi + +build-centaurid-env: + @if [ -z "$(docker images -q centauri/centaurid-upgrade-env 2> /dev/null)" ]; then \ + $(MAKE) -C contrib/centaurid-env centaurid-upgrade-env; \ + fi + +## Presiquites: build-cosmovisor-linux build-linux build-centaurid-env +localnet-start-upgrade: localnet-upgrade-stop build-cosmovisor-linux build-linux build-centaurid-env + bash contrib/updates/prepare_cosmovisor.sh $(BUILDDIR) ${TESTNET_NVAL} ${TESTNET_CHAINID} + docker-compose -f ./contrib/updates/docker-compose.yml up -d + @./contrib/updates/upgrade-test.sh + $(MAKE) localnet-upgrade-stop + +localnet-upgrade-stop: + docker-compose -f contrib/updates/docker-compose.yml down + rm -rf build + rm -rf _build \ No newline at end of file diff --git a/app/app.go b/app/app.go index 7fefaf55c..154d03224 100644 --- a/app/app.go +++ b/app/app.go @@ -199,6 +199,7 @@ var ( alliancemoduletypes.ModuleName: {authtypes.Minter, authtypes.Burner}, alliancemoduletypes.RewardsPoolName: nil, icatypes.ModuleName: nil, + wasmtypes.ModuleName: nil, // this line is used by starport scaffolding # stargate/app/maccPerms } ) @@ -266,7 +267,6 @@ func NewComposableApp( interfaceRegistry: interfaceRegistry, invCheckPeriod: invCheckPeriod, } - app.InitSpecialKeepers( appCodec, cdc, diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 3eb7bd482..0f60f8a44 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -459,9 +459,9 @@ func (*AppKeepers) initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *cod paramsKeeper.Subspace(crisistypes.ModuleName) paramsKeeper.Subspace(ibctransfertypes.ModuleName) paramsKeeper.Subspace(ratelimitmoduletypes.ModuleName) - paramsKeeper.Subspace(icqtypes.ModuleName) + paramsKeeper.Subspace(icqtypes.ModuleName).WithKeyTable(icqtypes.ParamKeyTable()) paramsKeeper.Subspace(ibchost.ModuleName) - paramsKeeper.Subspace(icahosttypes.SubModuleName) + paramsKeeper.Subspace(icahosttypes.SubModuleName).WithKeyTable(icahosttypes.ParamKeyTable()) paramsKeeper.Subspace(alliancemoduletypes.ModuleName) paramsKeeper.Subspace(wasmtypes.ModuleName) paramsKeeper.Subspace(transfermiddlewaretypes.ModuleName) diff --git a/cmd/centaurid/cmd/root.go b/cmd/centaurid/cmd/root.go index 2160899fa..8cb43313f 100644 --- a/cmd/centaurid/cmd/root.go +++ b/cmd/centaurid/cmd/root.go @@ -175,6 +175,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig app.EncodingConfig) { AddGenesisAccountCmd(app.DefaultNodeHome), tmcli.NewCompletionCmd(rootCmd, true), addDebugCommands(debug.Cmd()), + NewTestnetCmd(app.ModuleBasics, banktypes.GenesisBalancesIterator{}, genutiltypes.DefaultMessageValidator), debug.Cmd(), config.Cmd(), CovertPrefixAddr(), diff --git a/cmd/centaurid/cmd/testnet.go b/cmd/centaurid/cmd/testnet.go new file mode 100644 index 000000000..2411fe878 --- /dev/null +++ b/cmd/centaurid/cmd/testnet.go @@ -0,0 +1,532 @@ +package cmd + +// DONTCOVER + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + + "github.com/CosmWasm/wasmd/app" + + tmconfig "github.com/cometbft/cometbft/config" + tmos "github.com/cometbft/cometbft/libs/os" + tmrand "github.com/cometbft/cometbft/libs/rand" + "github.com/cometbft/cometbft/types" + tmtime "github.com/cometbft/cometbft/types/time" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/server" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + 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" +) + +var ( + flagNodeDirPrefix = "node-dir-prefix" + flagNumValidators = "v" + flagOutputDir = "output-dir" + flagNodeDaemonHome = "node-daemon-home" + flagStartingIPAddress = "starting-ip-address" + flagEnableLogging = "enable-logging" + flagGRPCAddress = "grpc.address" + flagRPCAddress = "rpc.address" + flagAPIAddress = "api.address" + flagPrintMnemonic = "print-mnemonic" + flagKeyAlgorithm = "algo" +) + +type initArgs struct { + algo string + chainID string + keyringBackend string + minGasPrices string + nodeDaemonHome string + nodeDirPrefix string + numValidators int + outputDir string + startingIPAddress string +} + +type startArgs struct { + algo string + apiAddress string + chainID string + enableLogging bool + grpcAddress string + minGasPrices string + numValidators int + outputDir string + printMnemonic bool + rpcAddress string +} + +func addTestnetFlagsToCmd(cmd *cobra.Command) { + cmd.Flags().Int(flagNumValidators, 4, "Number of validators to initialize the testnet with") + cmd.Flags().StringP(flagOutputDir, "o", "./.testnets", "Directory to store initialization data for the testnet") + cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.001uwhale)") + cmd.Flags().String(flagKeyAlgorithm, string(hd.Secp256k1Type), "Key signing algorithm to generate keys for") +} + +// NewTestnetCmd creates a root testnet command with subcommands to run an in-process testnet or initialize +// validator configuration files for running a multi-validator testnet in a separate process +func NewTestnetCmd(mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, validator genutiltypes.MessageValidator) *cobra.Command { + testnetCmd := &cobra.Command{ + Use: "testnet", + Short: "subcommands for starting or configuring local testnets", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + testnetCmd.AddCommand(testnetStartCmd()) + testnetCmd.AddCommand(testnetInitFilesCmd(mbm, genBalIterator, validator)) + + return testnetCmd +} + +// testnetInitFilesCmd returns a cmd to initialize all files for tendermint testnet and application +func testnetInitFilesCmd(mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, + validator genutiltypes.MessageValidator, +) *cobra.Command { + cmd := &cobra.Command{ + Use: "init-files", + Short: "Initialize config directories & files for a multi-validator testnet running locally via separate processes (e.g. Docker Compose or similar)", + Long: `init-files will setup "v" number of directories and populate each with +necessary files (private validator, genesis, config, etc.) for running "v" validator nodes. + +Booting up a network with these validator folders is intended to be used with Docker Compose, +or a similar setup where each node has a manually configurable IP address. + +Note, strict routability for addresses is turned off in the config file. + +Example: + simd testnet init-files --v 4 --output-dir ./.testnets --starting-ip-address 192.168.10.2 + `, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config + + args := initArgs{} + args.outputDir, _ = cmd.Flags().GetString(flagOutputDir) + args.keyringBackend, _ = cmd.Flags().GetString(flags.FlagKeyringBackend) + args.chainID, _ = cmd.Flags().GetString(flags.FlagChainID) + args.minGasPrices, _ = cmd.Flags().GetString(server.FlagMinGasPrices) + args.nodeDirPrefix, _ = cmd.Flags().GetString(flagNodeDirPrefix) + args.nodeDaemonHome, _ = cmd.Flags().GetString(flagNodeDaemonHome) + args.startingIPAddress, _ = cmd.Flags().GetString(flagStartingIPAddress) + args.numValidators, _ = cmd.Flags().GetInt(flagNumValidators) + args.algo, _ = cmd.Flags().GetString(flagKeyAlgorithm) + + return initTestnetFiles(clientCtx, cmd, config, mbm, genBalIterator, validator, args) + }, + } + + addTestnetFlagsToCmd(cmd) + cmd.Flags().String(flagNodeDirPrefix, "node", "Prefix the directory name for each node with (node results in node0, node1, ...)") + cmd.Flags().String(flagNodeDaemonHome, "simd", "Home directory of the node's daemon configuration") + cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + + return cmd +} + +// testnetStartCmd returns a cmd to start multi validator in-process testnet +func testnetStartCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "start", + Short: "Launch an in-process multi-validator testnet", + Long: `testnet will launch an in-process multi-validator testnet, +and generate "v" directories, populated with necessary validator configuration files +(private validator, genesis, config, etc.). + +Example: + simd testnet --v 4 --output-dir ./.testnets + `, + RunE: func(cmd *cobra.Command, _ []string) error { + args := startArgs{} + args.outputDir, _ = cmd.Flags().GetString(flagOutputDir) + args.chainID, _ = cmd.Flags().GetString(flags.FlagChainID) + args.minGasPrices, _ = cmd.Flags().GetString(server.FlagMinGasPrices) + args.numValidators, _ = cmd.Flags().GetInt(flagNumValidators) + args.algo, _ = cmd.Flags().GetString(flagKeyAlgorithm) + args.enableLogging, _ = cmd.Flags().GetBool(flagEnableLogging) + args.rpcAddress, _ = cmd.Flags().GetString(flagRPCAddress) + args.apiAddress, _ = cmd.Flags().GetString(flagAPIAddress) + args.grpcAddress, _ = cmd.Flags().GetString(flagGRPCAddress) + args.printMnemonic, _ = cmd.Flags().GetBool(flagPrintMnemonic) + + return startTestnet(cmd, args) + }, + } + + addTestnetFlagsToCmd(cmd) + cmd.Flags().Bool(flagEnableLogging, false, "Enable INFO logging of tendermint validator nodes") + cmd.Flags().String(flagRPCAddress, "tcp://0.0.0.0:26657", "the RPC address to listen on") + cmd.Flags().String(flagAPIAddress, "tcp://0.0.0.0:1317", "the address to listen on for REST API") + cmd.Flags().String(flagGRPCAddress, "0.0.0.0:9090", "the gRPC server address to listen on") + cmd.Flags().Bool(flagPrintMnemonic, true, "print mnemonic of first validator to stdout for manual testing") + return cmd +} + +const nodeDirPerm = 0o755 + +// initTestnetFiles initializes testnet files for a testnet to be run in a separate process +func initTestnetFiles( + clientCtx client.Context, + cmd *cobra.Command, + nodeConfig *tmconfig.Config, + mbm module.BasicManager, + genBalIterator banktypes.GenesisBalancesIterator, + validator genutiltypes.MessageValidator, + args initArgs, +) error { + if args.chainID == "" { + args.chainID = "chain-" + tmrand.Str(6) + } + nodeIDs := make([]string, args.numValidators) + valPubKeys := make([]cryptotypes.PubKey, args.numValidators) + + migalooConfig := srvconfig.DefaultConfig() + migalooConfig.MinGasPrices = args.minGasPrices + migalooConfig.API.Enable = true + migalooConfig.Telemetry.Enabled = true + migalooConfig.Telemetry.PrometheusRetentionTime = 60 + migalooConfig.Telemetry.EnableHostnameLabel = false + migalooConfig.Telemetry.GlobalLabels = [][]string{{"chain_id", args.chainID}} + + var ( + genAccounts []authtypes.GenesisAccount + genBalances []banktypes.Balance + genFiles []string + ) + + inBuf := bufio.NewReader(cmd.InOrStdin()) + // generate private keys, node IDs, and initial transactions + for i := 0; i < args.numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", args.nodeDirPrefix, i) + nodeDir := filepath.Join(args.outputDir, nodeDirName, args.nodeDaemonHome) + gentxsDir := filepath.Join(args.outputDir, "gentxs") + + nodeConfig.SetRoot(nodeDir) + nodeConfig.Moniker = nodeDirName + nodeConfig.RPC.ListenAddress = "tcp://0.0.0.0:26657" + + if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil { + _ = os.RemoveAll(args.outputDir) + return err + } + + ip, err := getIP(i, args.startingIPAddress) + if err != nil { + _ = os.RemoveAll(args.outputDir) + return err + } + + nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(nodeConfig) + if err != nil { + _ = os.RemoveAll(args.outputDir) + return err + } + + memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) + genFiles = append(genFiles, nodeConfig.GenesisFile()) + + kb, err := keyring.New(sdk.KeyringServiceName(), args.keyringBackend, nodeDir, inBuf, clientCtx.Codec) + if err != nil { + return err + } + + keyringAlgos, _ := kb.SupportedAlgorithms() + algo, err := keyring.NewSigningAlgoFromString(args.algo, keyringAlgos) + if err != nil { + return err + } + + addr, secret, err := testutil.GenerateSaveCoinKey(kb, nodeDirName, "", true, algo) + if err != nil { + _ = os.RemoveAll(args.outputDir) + return err + } + + info := map[string]string{"secret": secret} + + cliPrint, err := json.Marshal(info) + if err != nil { + return err + } + + // save private key seed words + if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), nodeDir, cliPrint); err != nil { + return err + } + + accTokens := sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction) + accStakingTokens := sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction) + coins := sdk.Coins{ + sdk.NewCoin("testtoken", accTokens), + sdk.NewCoin(sdk.DefaultBondDenom, accStakingTokens), + } + + genBalances = append(genBalances, banktypes.Balance{Address: addr.String(), Coins: coins.Sort()}) + genAccounts = append(genAccounts, authtypes.NewBaseAccount(addr, nil, 0, 0)) + + valTokens := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) + createValMsg, err := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + valPubKeys[i], + sdk.NewCoin(sdk.DefaultBondDenom, valTokens), + stakingtypes.NewDescription(nodeDirName, "", "", "", ""), + stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), + sdk.OneInt(), + ) + if err != nil { + return err + } + + txBuilder := clientCtx.TxConfig.NewTxBuilder() + if err := txBuilder.SetMsgs(createValMsg); err != nil { + return err + } + + txBuilder.SetMemo(memo) + + txFactory := tx.Factory{} + txFactory = txFactory. + WithChainID(args.chainID). + WithMemo(memo). + WithKeybase(kb). + WithTxConfig(clientCtx.TxConfig) + + if err := tx.Sign(txFactory, nodeDirName, txBuilder, true); err != nil { + return err + } + + txBz, err := clientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + if err != nil { + return err + } + + if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBz); err != nil { + return err + } + + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), migalooConfig) + } + + if err := initGenFiles(clientCtx, mbm, args.chainID, genAccounts, genBalances, genFiles, args.numValidators); err != nil { + return err + } + + err := collectGenFiles( + clientCtx, nodeConfig, args.chainID, nodeIDs, valPubKeys, args.numValidators, + args.outputDir, args.nodeDirPrefix, args.nodeDaemonHome, genBalIterator, validator, + ) + if err != nil { + return err + } + + cmd.PrintErrf("Successfully initialized %d node directories\n", args.numValidators) + return nil +} + +func initGenFiles( + clientCtx client.Context, mbm module.BasicManager, chainID string, + genAccounts []authtypes.GenesisAccount, genBalances []banktypes.Balance, + genFiles []string, numValidators int, +) error { + appGenState := mbm.DefaultGenesis(clientCtx.Codec) + + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + clientCtx.Codec.MustUnmarshalJSON(appGenState[authtypes.ModuleName], &authGenState) + + accounts, err := authtypes.PackAccounts(genAccounts) + if err != nil { + return err + } + + authGenState.Accounts = accounts + appGenState[authtypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(&authGenState) + + // set the balances in the genesis state + var bankGenState banktypes.GenesisState + clientCtx.Codec.MustUnmarshalJSON(appGenState[banktypes.ModuleName], &bankGenState) + + bankGenState.Balances = banktypes.SanitizeGenesisBalances(genBalances) + for _, bal := range bankGenState.Balances { + bankGenState.Supply = bankGenState.Supply.Add(bal.Coins...) + } + appGenState[banktypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(&bankGenState) + + appGenStateJSON, err := json.MarshalIndent(appGenState, "", " ") + if err != nil { + return err + } + + genDoc := types.GenesisDoc{ + ChainID: chainID, + AppState: appGenStateJSON, + Validators: nil, + } + + // generate empty genesis files for each validator and save + for i := 0; i < numValidators; i++ { + if err := genDoc.SaveAs(genFiles[i]); err != nil { + return err + } + } + return nil +} + +func collectGenFiles( + clientCtx client.Context, nodeConfig *tmconfig.Config, chainID string, + nodeIDs []string, valPubKeys []cryptotypes.PubKey, numValidators int, + outputDir, nodeDirPrefix, nodeDaemonHome string, genBalIterator banktypes.GenesisBalancesIterator, + validator genutiltypes.MessageValidator, +) error { + var appState json.RawMessage + genTime := tmtime.Now() + + for i := 0; i < numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + gentxsDir := filepath.Join(outputDir, "gentxs") + nodeConfig.Moniker = nodeDirName + + nodeConfig.SetRoot(nodeDir) + + nodeID, valPubKey := nodeIDs[i], valPubKeys[i] + initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, valPubKey) + + genDoc, err := types.GenesisDocFromFile(nodeConfig.GenesisFile()) + if err != nil { + return err + } + + nodeAppState, err := genutil.GenAppStateFromConfig(clientCtx.Codec, clientCtx.TxConfig, nodeConfig, initCfg, *genDoc, genBalIterator, validator) + if err != nil { + return err + } + + if appState == nil { + // set the canonical application state (they should not differ) + appState = nodeAppState + } + + genFile := nodeConfig.GenesisFile() + + // overwrite each validator's genesis file to have a canonical genesis time + if err := genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime); err != nil { + return err + } + } + + return nil +} + +func getIP(i int, startingIPAddr string) (ip string, err error) { + if len(startingIPAddr) == 0 { + ip, err = server.ExternalIP() + if err != nil { + return "", err + } + return ip, nil + } + return calculateIP(startingIPAddr, i) +} + +func calculateIP(ip string, i int) (string, error) { + ipv4 := net.ParseIP(ip).To4() + if ipv4 == nil { + return "", fmt.Errorf("%v: non ipv4 address", ip) + } + + for j := 0; j < i; j++ { + ipv4[3]++ + } + + return ipv4.String(), nil +} + +func writeFile(name string, dir string, contents []byte) error { + writePath := filepath.Join(dir) //nolint:gocritic + file := filepath.Join(writePath, name) + + err := tmos.EnsureDir(writePath, 0o755) + if err != nil { + return err + } + + err = os.WriteFile(file, contents, 0o644) //nolint: gosec + if err != nil { + return err + } + + return nil +} + +// startTestnet starts an in-process testnet +func startTestnet(cmd *cobra.Command, args startArgs) error { + networkConfig := network.DefaultConfig(app.NewTestNetworkFixture) + + // Default networkConfig.ChainID is random, and we should only override it if chainID provided + // is non-empty + if args.chainID != "" { + networkConfig.ChainID = args.chainID + } + networkConfig.SigningAlgo = args.algo + networkConfig.MinGasPrices = args.minGasPrices + networkConfig.NumValidators = args.numValidators + networkConfig.EnableTMLogging = args.enableLogging + networkConfig.RPCAddress = args.rpcAddress + networkConfig.APIAddress = args.apiAddress + networkConfig.GRPCAddress = args.grpcAddress + networkConfig.PrintMnemonic = args.printMnemonic + networkLogger := network.NewCLILogger(cmd) + + baseDir := fmt.Sprintf("%s/%s", args.outputDir, networkConfig.ChainID) + if _, err := os.Stat(baseDir); !os.IsNotExist(err) { + return fmt.Errorf( + "testnests directory already exists for chain-id '%s': %s, please remove or select a new --chain-id", + networkConfig.ChainID, baseDir) + } + + testnet, err := network.New(networkLogger, baseDir, networkConfig) + if err != nil { + return err + } + + if _, err := testnet.WaitForHeight(1); err != nil { + return err + } + cmd.Println("press the Enter Key to terminate") + if _, err := fmt.Scanln(); err != nil { // wait for Enter Key + return err + } + testnet.Cleanup() + + return nil +} diff --git a/contrib/centaurid-env/Dockerfile b/contrib/centaurid-env/Dockerfile new file mode 100644 index 000000000..8f510eef8 --- /dev/null +++ b/contrib/centaurid-env/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:18.04 + +RUN apt-get update && \ + apt-get -y upgrade && \ + apt-get -y install curl jq file + +ARG entrypoint=entrypoint.sh + +VOLUME /centaurid +WORKDIR /centaurid +EXPOSE 26656 26657 +ENTRYPOINT ["/usr/bin/entrypoint.sh"] +CMD ["start", "--log_format", "plain", "--p2p.seeds", ""] +STOPSIGNAL SIGTERM + +COPY ${entrypoint} /usr/bin/entrypoint.sh +RUN chmod +x /usr/bin/entrypoint.sh \ No newline at end of file diff --git a/contrib/centaurid-env/Makefile b/contrib/centaurid-env/Makefile new file mode 100644 index 000000000..90f7eec83 --- /dev/null +++ b/contrib/centaurid-env/Makefile @@ -0,0 +1,6 @@ +all: centaurid-upgrade-env + +centaurid-upgrade-env: + docker build --platform linux/amd64 --no-cache --build-arg entrypoint=cosmovisor-entrypoint.sh --tag centauri/centaurid-upgrade-env . + +.PHONY: all centaurid-upgrade-env diff --git a/contrib/centaurid-env/cosmovisor-entrypoint.sh b/contrib/centaurid-env/cosmovisor-entrypoint.sh new file mode 100644 index 000000000..ce4283176 --- /dev/null +++ b/contrib/centaurid-env/cosmovisor-entrypoint.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env sh + +BINARY=/centaurid/${BINARY:-cosmovisor} +ID=${ID:-0} +LOG=${LOG:-centaurid.log} + +if ! [ -f "${BINARY}" ]; then + echo "The binary $(basename "${BINARY}") cannot be found. Please add the binary to the shared folder. Please use the BINARY environment variable if the name of the binary is not 'centaurid'" + exit 1 +fi + +BINARY_CHECK="$(file "$BINARY" | grep 'ELF 64-bit LSB executable, x86-64')" + +if [ -z "${BINARY_CHECK}" ]; then + echo "Binary needs to be OS linux, ARCH amd64" + exit 1 +fi + +export CENTAURID_HOME="/centaurid/node${ID}/centaurid" + +if [ -d "$(dirname "${CENTAURID_HOME}"/"${LOG}")" ]; then + "${BINARY}" run "$@" --home "${CENTAURID_HOME}" | tee "${CENTAURID_HOME}/${LOG}" +else + "${BINARY}" run "$@" --home "${CENTAURID_HOME}" +fi \ No newline at end of file diff --git a/contrib/updates/Dockerfile.cosmovisor b/contrib/updates/Dockerfile.cosmovisor new file mode 100644 index 000000000..6f7f2949a --- /dev/null +++ b/contrib/updates/Dockerfile.cosmovisor @@ -0,0 +1,8 @@ +FROM golang:1.20-alpine + +RUN set -eux; apk add --no-cache ca-certificates build-base; + +# make cosmovisor statically linked +RUN go install -ldflags '-w -s -linkmode=external -extldflags "-Wl,-z,muldefs -static"' -trimpath cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest + +ENTRYPOINT [ "/bin/sh" ] \ No newline at end of file diff --git a/contrib/updates/Makefile b/contrib/updates/Makefile new file mode 100644 index 000000000..0f3fde8a5 --- /dev/null +++ b/contrib/updates/Makefile @@ -0,0 +1,10 @@ +all: build-cosmovisor-linux + +build-cosmovisor-linux: + mkdir -p $(BUILDDIR) + docker build --platform linux/amd64 --no-cache --tag centauri/centauri.cosmovisor-binary --file Dockerfile.cosmovisor . + docker create --platform linux/amd64 --name temp centauri/centauri.cosmovisor-binary:latest + docker cp temp:/go/bin/cosmovisor $(BUILDDIR)/ + docker rm temp + +.PHONY: all build-cosmovisor-linux \ No newline at end of file diff --git a/contrib/updates/docker-compose.yml b/contrib/updates/docker-compose.yml new file mode 100644 index 000000000..20050613e --- /dev/null +++ b/contrib/updates/docker-compose.yml @@ -0,0 +1,125 @@ +version: '3' + +services: + centauridnode0: + container_name: centauridnode0 + platform: linux/amd64 + image: "centauri/centaurid-upgrade-env" + ports: + - "9090:9090" + - "26656-26657:26656-26657" + environment: + - ID=0 + - LOG=centaurid.log + - DAEMON_HOME=/centaurid/node0/centaurid + - DAEMON_NAME=centaurid + - DAEMON_RESTART_AFTER_UPGRADE=true + volumes: + - ../../build:/centaurid:Z + networks: + localnet: + ipv4_address: 192.168.0.2 + + centauridnode1: + container_name: centauridnode1 + platform: linux/amd64 + image: "centauri/centaurid-upgrade-env" + ports: + - "9091:9090" + - "26659-26660:26656-26657" + environment: + - ID=1 + - LOG=centaurid.log + - DAEMON_HOME=/centaurid/node1/centaurid + - DAEMON_NAME=centaurid + - DAEMON_RESTART_AFTER_UPGRADE=true + volumes: + - ../../build:/centaurid:Z + networks: + localnet: + ipv4_address: 192.168.0.3 + + centauridnode2: + container_name: centauridnode2 + platform: linux/amd64 + image: "centauri/centaurid-upgrade-env" + environment: + - ID=2 + - LOG=centaurid.log + - DAEMON_HOME=/centaurid/node2/centaurid + - DAEMON_NAME=centaurid + - DAEMON_RESTART_AFTER_UPGRADE=true + ports: + - "9092:9090" + - "26661-26662:26656-26657" + volumes: + - ../../build:/centaurid:Z + networks: + localnet: + ipv4_address: 192.168.0.4 + + centauridnode3: + container_name: centauridnode3 + platform: linux/amd64 + image: "centauri/centaurid-upgrade-env" + environment: + - ID=3 + - LOG=centaurid.log + - DAEMON_HOME=/centaurid/node3/centaurid + - DAEMON_NAME=centaurid + - DAEMON_RESTART_AFTER_UPGRADE=true + ports: + - "9093:9090" + - "26663-26664:26656-26657" + volumes: + - ../../build:/centaurid:Z + networks: + localnet: + ipv4_address: 192.168.0.5 + + centauridnode4: + container_name: centauridnode4 + platform: linux/amd64 + image: "centauri/centaurid-upgrade-env" + environment: + - ID=4 + - LOG=centaurid.log + - DAEMON_HOME=/centaurid/node4/centaurid + - DAEMON_NAME=centaurid + - DAEMON_RESTART_AFTER_UPGRADE=true + ports: + - "9094:9090" + - "26665-26666:26656-26657" + volumes: + - ../../build:/centaurid:Z + networks: + localnet: + ipv4_address: 192.168.0.6 + + centauridnode5: + container_name: centauridnode5 + platform: linux/amd64 + image: "centauri/centaurid-upgrade-env" + environment: + - ID=5 + - LOG=centaurid.log + - DAEMON_HOME=/centaurid/node5/centaurid + - DAEMON_NAME=centaurid + - DAEMON_RESTART_AFTER_UPGRADE=true + ports: + - "9095:9090" + - "26667-26668:26656-26657" + volumes: + - ../../build:/centaurid:Z + networks: + localnet: + ipv4_address: 192.168.0.7 + +networks: + localnet: + driver: bridge + ipam: + driver: default + config: + - + subnet: 192.168.0.0/16 \ No newline at end of file diff --git a/contrib/updates/prepare_cosmovisor.sh b/contrib/updates/prepare_cosmovisor.sh new file mode 100644 index 000000000..0fe7ec5e1 --- /dev/null +++ b/contrib/updates/prepare_cosmovisor.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# this bash will prepare cosmosvisor to the build folder so that it can perform upgrade +# this script is supposed to be run by Makefile + +# These fields should be fetched automatically in the future +# Need to do more upgrade to see upgrade patterns +OLD_VERSION=v6.4.x +# this command will retrieve the folder with the largest number in format v +SOFTWARE_UPGRADE_NAME="v7" +BUILDDIR=$1 +TESTNET_NVAL=$2 +TESTNET_CHAINID=$3 + +# check if BUILDDIR is set +if [ -z "$BUILDDIR" ]; then + echo "BUILDDIR is not set" + exit 1 +fi + +# install old binary if not exist +if [ ! -f "_build/$OLD_VERSION.zip" ] &> /dev/null +then + mkdir -p _build/old + # This archive have cmd testnet for upgrade testing + wget -c "https://github.com/tungle-notional/composable-old/archive/refs/tags/v6.4.x_old.zip" -O _build/${OLD_VERSION}.zip + unzip _build/${OLD_VERSION}.zip -d _build +fi + + +if [ ! -f "$BUILDDIR/old/centaurid" ] &> /dev/null +then + if [ ! "$(docker images -q centauri/centaurid.binary.old 2> /dev/null)" ]; then + docker build --platform linux/amd64 --no-cache --build-arg source=./_build/composable-cosmos-${OLD_VERSION:1}/ --tag centauri/centaurid.binary.old ./_build/composable-cosmos-${OLD_VERSION:1} + fi + docker create --platform linux/amd64 --name old-temp centauri/centaurid.binary.old:latest + mkdir -p $BUILDDIR/old + docker cp old-temp:/bin/centaurid $BUILDDIR/old/ + docker rm old-temp +fi + +echo "init-files" +# prepare cosmovisor config in TESTNET_NVAL nodes +if [ ! -f "$BUILDDIR/node0/centaurid/config/genesis.json" ]; then docker run --rm \ + -v $BUILDDIR:/centaurid:Z \ + --platform linux/amd64 \ + --entrypoint /centaurid/old/centaurid \ + centauri/centaurid-upgrade-env testnet init-files --v $TESTNET_NVAL --chain-id $TESTNET_CHAINID -o . --starting-ip-address 192.168.0.2 --minimum-gas-prices "0stake" --node-daemon-home centaurid --keyring-backend=test --home=temp; \ +fi + +for (( i=0; i<$TESTNET_NVAL; i++ )); do + CURRENT=$BUILDDIR/node$i/centaurid + echo "Change voting_period" + # change gov params voting_period + jq '.app_state["gov"]["params"]["voting_period"] = "50s"' $CURRENT/config/genesis.json > $CURRENT/config/genesis.json.tmp && mv $CURRENT/config/genesis.json.tmp $CURRENT/config/genesis.json + + docker run --rm \ + -v $BUILDDIR:/centaurid:Z \ + -e DAEMON_HOME=/centaurid/node$i/centaurid \ + -e DAEMON_NAME=centaurid \ + -e DAEMON_RESTART_AFTER_UPGRADE=true \ + --entrypoint /centaurid/cosmovisor \ + --platform linux/amd64 \ + centauri/centaurid-upgrade-env init /centaurid/old/centaurid + mkdir -p $CURRENT/cosmovisor/upgrades/$SOFTWARE_UPGRADE_NAME/bin + cp $BUILDDIR/centaurid $CURRENT/cosmovisor/upgrades/$SOFTWARE_UPGRADE_NAME/bin + touch $CURRENT/cosmovisor/upgrades/$SOFTWARE_UPGRADE_NAME/upgrade-info.json +done \ No newline at end of file