From 11bf84333c4dd3c30ee131d93b8c8c3f3c1cc26a Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 24 Aug 2023 08:02:26 +0000 Subject: [PATCH] Support e2e tests for callback module SimApp (#4406) (cherry picked from commit 9a22edec02297d6c8712148d24c34f3002fc09fc) --- .../build-callbacks-simd-image-from-tag.yml | 35 ++ modules/apps/callbacks/Dockerfile | 28 ++ .../apps/callbacks/testing/simapp/README.md | 7 + .../callbacks/testing/simapp/simd/cmd/root.go | 305 ++++++++++++++++++ .../callbacks/testing/simapp/simd/main.go | 24 ++ 5 files changed, 399 insertions(+) create mode 100644 .github/workflows/build-callbacks-simd-image-from-tag.yml create mode 100644 modules/apps/callbacks/Dockerfile create mode 100644 modules/apps/callbacks/testing/simapp/README.md create mode 100644 modules/apps/callbacks/testing/simapp/simd/cmd/root.go create mode 100644 modules/apps/callbacks/testing/simapp/simd/main.go diff --git a/.github/workflows/build-callbacks-simd-image-from-tag.yml b/.github/workflows/build-callbacks-simd-image-from-tag.yml new file mode 100644 index 00000000000..2d1bb4bdfdb --- /dev/null +++ b/.github/workflows/build-callbacks-simd-image-from-tag.yml @@ -0,0 +1,35 @@ +name: Build Callbacks Simd Image +on: + workflow_dispatch: + inputs: + tag: + description: 'The tag of the image to build' + required: true + type: string + +env: + REGISTRY: ghcr.io + ORG: cosmos + IMAGE_NAME: ibc-go-callbacks-simd + GIT_TAG: "${{ inputs.tag }}" + +jobs: + build-image-at-tag: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: "${{ env.GIT_TAG }}" + fetch-depth: 0 + - name: Log in to the Container registry + uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build image + run: | + # remove any `/` characters from the docker tag and replace them with a - + docker_tag="$(echo $GIT_TAG | sed 's/\//-/')" + docker build . -t "${REGISTRY}/${ORG}/${IMAGE_NAME}:${docker_tag}" -f modules/apps/callbacks/Dockerfile + docker push "${REGISTRY}/${ORG}/${IMAGE_NAME}:${docker_tag}" diff --git a/modules/apps/callbacks/Dockerfile b/modules/apps/callbacks/Dockerfile new file mode 100644 index 00000000000..d81cee1389e --- /dev/null +++ b/modules/apps/callbacks/Dockerfile @@ -0,0 +1,28 @@ +FROM golang:1.21-alpine3.18 as builder + +RUN set -eux; apk add --no-cache git libusb-dev linux-headers gcc musl-dev make; + +ENV GOPATH="" +ENV GOMODULE="on" + +# Copy relevant files before go mod download. Replace directives to local paths break if local +# files are not copied before go mod download. +ADD internal internal +ADD testing testing +ADD modules modules +ADD LICENSE LICENSE + +COPY go.mod . +COPY go.sum . + +WORKDIR modules/apps/callbacks + +RUN go mod download + +RUN GOOS=linux GOARCH=amd64 LEDGER_ENABLED=false go build -mod=readonly -tags "netgo ledger" -ldflags '-X github.com/cosmos/cosmos-sdk/version.Name=sim -X github.com/cosmos/cosmos-sdk/version.AppName=simd -X github.com/cosmos/cosmos-sdk/version.Version= -X github.com/cosmos/cosmos-sdk/version.Commit= -X "github.com/cosmos/cosmos-sdk/version.BuildTags=netgo ledger," -w -s' -trimpath -o /go/build/ ./... + +FROM alpine:3.18 + +COPY --from=builder /go/build/simd /bin/simd + +ENTRYPOINT ["simd"] diff --git a/modules/apps/callbacks/testing/simapp/README.md b/modules/apps/callbacks/testing/simapp/README.md new file mode 100644 index 00000000000..7ee00030c45 --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/README.md @@ -0,0 +1,7 @@ +# Callbacks Testing SimApp + +This testing directory is a duplicate of the ibc-go testing directory. +It is only here as a way of creating a separate SimApp binary to avoid introducing a dependency on the callbacks +module from within ibc-go. + +The simapp can be built with the workflow found [here](../../../../../.github/workflows/build-callbacks-simd-image-from-tag.yml). diff --git a/modules/apps/callbacks/testing/simapp/simd/cmd/root.go b/modules/apps/callbacks/testing/simapp/simd/cmd/root.go new file mode 100644 index 00000000000..ef4106c1337 --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/simd/cmd/root.go @@ -0,0 +1,305 @@ +package cmd + +import ( + "errors" + "io" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/config" + "github.com/cosmos/cosmos-sdk/client/debug" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/pruning" + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/client/snapshot" + "github.com/cosmos/cosmos-sdk/server" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/crisis" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + + dbm "github.com/cometbft/cometbft-db" + cmtcfg "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/libs/log" + + "github.com/cosmos/ibc-go/modules/apps/callbacks/testing/simapp" + "github.com/cosmos/ibc-go/modules/apps/callbacks/testing/simapp/params" +) + +// NewRootCmd creates a new root command for simd. It is called once in the +// main function. +func NewRootCmd() *cobra.Command { + // we "pre"-instantiate the application for getting the injected/configured encoding configuration + tempApp := simapp.NewSimApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(simapp.DefaultNodeHome)) + encodingConfig := params.EncodingConfig{ + InterfaceRegistry: tempApp.InterfaceRegistry(), + Codec: tempApp.AppCodec(), + TxConfig: tempApp.TxConfig(), + Amino: tempApp.LegacyAmino(), + } + + initClientCtx := client.Context{}. + WithCodec(encodingConfig.Codec). + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithInput(os.Stdin). + WithAccountRetriever(types.AccountRetriever{}). + WithHomeDir(simapp.DefaultNodeHome). + WithViper("") // In simapp, we don't use any prefix for env variables. + + rootCmd := &cobra.Command{ + Use: "simd", + Short: "simulation app", + PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { + // set the default command outputs + cmd.SetOut(cmd.OutOrStdout()) + cmd.SetErr(cmd.ErrOrStderr()) + + initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags()) + if err != nil { + return err + } + + initClientCtx, err = config.ReadFromClientConfig(initClientCtx) + if err != nil { + return err + } + + if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { + return err + } + + customAppTemplate, customAppConfig := initAppConfig() + customTMConfig := initTendermintConfig() + + return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customTMConfig) + }, + } + + initRootCmd(rootCmd, encodingConfig) + + return rootCmd +} + +// initTendermintConfig helps to override default Tendermint Config values. +// return cmtcfg.DefaultConfig if no custom configuration is required for the application. +func initTendermintConfig() *cmtcfg.Config { + cfg := cmtcfg.DefaultConfig() + + // these values put a higher strain on node memory + // cfg.P2P.MaxNumInboundPeers = 100 + // cfg.P2P.MaxNumOutboundPeers = 40 + + return cfg +} + +// initAppConfig helps to override default appConfig template and configs. +// return "", nil if no custom configuration is required for the application. +func initAppConfig() (string, interface{}) { + // The following code snippet is just for reference. + + // WASMConfig defines configuration for the wasm module. + type WASMConfig struct { + // This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries + QueryGasLimit uint64 `mapstructure:"query_gas_limit"` + + // Address defines the gRPC-web server to listen on + LruSize uint64 `mapstructure:"lru_size"` + } + + type CustomAppConfig struct { + serverconfig.Config + + WASM WASMConfig `mapstructure:"wasm"` + } + + // Optionally allow the chain developer to overwrite the SDK's default + // server config. + srvCfg := serverconfig.DefaultConfig() + // The SDK's default minimum gas price is set to "" (empty value) inside + // app.toml. If left empty by validators, the node will halt on startup. + // However, the chain developer can set a default app.toml value for their + // validators here. + // + // In summary: + // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their + // own app.toml config, + // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their + // own app.toml to override, or use this default value. + // + // In simapp, we set the min gas prices to 0. + srvCfg.MinGasPrices = "0stake" + // srvCfg.BaseConfig.IAVLDisableFastNode = true // disable fastnode by default + + customAppConfig := CustomAppConfig{ + Config: *srvCfg, + WASM: WASMConfig{ + LruSize: 1, + QueryGasLimit: 300000, + }, + } + + customAppTemplate := serverconfig.DefaultConfigTemplate + ` +[wasm] +# This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries +query_gas_limit = 300000 +# This is the number of wasm vm instances we keep cached in memory for speed-up +# Warning: this is currently unstable and may lead to crashes, best to keep for 0 unless testing locally +lru_size = 0` + + return customAppTemplate, customAppConfig +} + +func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { + cfg := sdk.GetConfig() + cfg.Seal() + + rootCmd.AddCommand( + genutilcli.InitCmd(simapp.ModuleBasics, simapp.DefaultNodeHome), + debug.Cmd(), + config.Cmd(), + pruning.PruningCmd(newApp), + snapshot.Cmd(newApp), + ) + + server.AddCommands(rootCmd, simapp.DefaultNodeHome, newApp, appExport, addModuleInitFlags) + + // add keybase, auxiliary RPC, query, genesis, and tx child commands + rootCmd.AddCommand( + rpc.StatusCommand(), + genesisCommand(encodingConfig), + queryCommand(), + txCommand(), + keys.Commands(simapp.DefaultNodeHome), + ) +} + +func addModuleInitFlags(startCmd *cobra.Command) { + crisis.AddModuleInitFlags(startCmd) +} + +// genesisCommand builds genesis-related `simd genesis` command. Users may provide application specific commands as a parameter +func genesisCommand(encodingConfig params.EncodingConfig, cmds ...*cobra.Command) *cobra.Command { + cmd := genutilcli.GenesisCoreCommand(encodingConfig.TxConfig, simapp.ModuleBasics, simapp.DefaultNodeHome) + + for _, subCmd := range cmds { + cmd.AddCommand(subCmd) + } + return cmd +} + +func queryCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "query", + Aliases: []string{"q"}, + Short: "Querying subcommands", + DisableFlagParsing: false, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + cmd.AddCommand( + authcmd.GetAccountCmd(), + rpc.ValidatorCommand(), + rpc.BlockCommand(), + authcmd.QueryTxsByEventsCmd(), + authcmd.QueryTxCmd(), + ) + + simapp.ModuleBasics.AddQueryCommands(cmd) + + 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.GetAuxToFeeCommand(), + ) + + simapp.ModuleBasics.AddTxCommands(cmd) + + return cmd +} + +// newApp creates the application +func newApp( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + appOpts servertypes.AppOptions, +) servertypes.Application { + baseappOptions := server.DefaultBaseappOptions(appOpts) + + return simapp.NewSimApp( + logger, db, traceStore, true, + appOpts, + baseappOptions..., + ) +} + +// appExport creates a new simapp (optionally at a given height) and exports state. +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) { + var simApp *simapp.SimApp + + // this check is necessary as we use the flag in x/upgrade. + // we can exit more gracefully by checking the flag here. + 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") + } + + // overwrite the FlagInvCheckPeriod + viperAppOpts.Set(server.FlagInvCheckPeriod, 1) + appOpts = viperAppOpts + + if height != -1 { + simApp = simapp.NewSimApp(logger, db, traceStore, false, appOpts) + + if err := simApp.LoadHeight(height); err != nil { + return servertypes.ExportedApp{}, err + } + } else { + simApp = simapp.NewSimApp(logger, db, traceStore, true, appOpts) + } + + return simApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport) +} diff --git a/modules/apps/callbacks/testing/simapp/simd/main.go b/modules/apps/callbacks/testing/simapp/simd/main.go new file mode 100644 index 00000000000..632c87573e5 --- /dev/null +++ b/modules/apps/callbacks/testing/simapp/simd/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/server" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + + "github.com/cosmos/ibc-go/modules/apps/callbacks/testing/simapp" + "github.com/cosmos/ibc-go/modules/apps/callbacks/testing/simapp/simd/cmd" +) + +func main() { + rootCmd := cmd.NewRootCmd() + if err := svrcmd.Execute(rootCmd, "", simapp.DefaultNodeHome); err != nil { + switch e := err.(type) { + case server.ErrorCode: + os.Exit(e.Code) + + default: + os.Exit(1) + } + } +}