diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index a81eae8c..7e029c31 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -71,6 +71,17 @@ jobs: - name: Publish to GitHub Packages uses: docker/build-push-action@v4 with: + file: consumer.Dockerfile platforms: linux/amd64,linux/arm64 push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} + tags: | + ghcr.io/osmosis-labs/meshconsumerd:latest + + - name: Publish to GitHub Packages + uses: docker/build-push-action@v4 + with: + file: provider.Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: | + ghcr.io/osmosis-labs/meshproviderd:latest diff --git a/Dockerfile b/consumer.Dockerfile similarity index 83% rename from Dockerfile rename to consumer.Dockerfile index 7b6c88e3..1ae48a4c 100644 --- a/Dockerfile +++ b/consumer.Dockerfile @@ -21,14 +21,14 @@ COPY . /code # then log output of file /code/bin/meshd # then ensure static linking RUN cd demo/ && LEDGER_ENABLED=false BUILD_TAGS=muslc LINK_STATICALLY=true make build \ - && file /code/demo/build/meshd \ + && file /code/demo/build/meshconsumerd \ && echo "Ensuring binary is statically linked ..." \ - && (file /code/demo/build/meshd | grep "statically linked") + && (file /code/demo/build/meshconsumerd | grep "statically linked") # -------------------------------------------------------- FROM alpine:3.17 -COPY --from=go-builder /code/demo/build/meshd /usr/bin/meshd +COPY --from=go-builder /code/demo/build/meshconsumerd /usr/bin/meshconsumerd # Install dependencies used for Starship RUN apk add --no-cache curl make bash jq sed @@ -38,4 +38,4 @@ WORKDIR /opt # rest server, tendermint p2p, tendermint rpc EXPOSE 1317 26656 26657 -CMD ["/usr/bin/meshd", "version"] +CMD ["/usr/bin/meshconsumerd", "version"] diff --git a/demo/Makefile b/demo/Makefile index aece59aa..e5c6cc91 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -78,23 +78,30 @@ ifeq ($(OS),Windows_NT) $(error mesh demo server not supported) exit 1 else - go build -mod=readonly $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APPNAME) ./cmd/meshd + go build -mod=readonly $(BUILD_FLAGS) -o $(BUILD_DIR)/meshconsumerd ./cmd/meshconsumerd + go build -mod=readonly $(BUILD_FLAGS) -o $(BUILD_DIR)/meshproviderd ./cmd/meshproviderd endif build-vendored: - go build -mod=vendor $(BUILD_FLAGS) -o $(BUILD_DIR)/$(APPNAME) ./cmd/meshd + go build -mod=vendor $(BUILD_FLAGS) -o $(BUILD_DIR)/meshconsumerd ./cmd/meshconsumerd + go build -mod=vendor $(BUILD_FLAGS) -o $(BUILD_DIR)/meshproviderd ./cmd/meshproviderd build-linux-static: go mod vendor # quick hack to make local dependencies work in Docker mkdir -p $(BUILD_DIR) - docker build --tag osmosis/meshd:local ./ - docker create --name meshd_temp osmosis/meshd:local - docker cp meshd_temp:/usr/bin/meshd $(BUILD_DIR)/ + docker build --tag osmosis/meshconsumerd:local -f consumer.Dockerfile ./ + docker build --tag osmosis/meshproviderd:local -f provider.Dockerfile ./ + docker create --name meshd_temp osmosis/meshconsumerd:local + docker cp meshd_temp:/usr/bin/meshconsumerd $(BUILD_DIR)/ + docker rm -f meshd_temp + docker create --name meshd_temp osmosis/meshproviderd:local + docker cp meshd_temp:/usr/bin/meshproviderd $(BUILD_DIR)/ docker rm -f meshd_temp rm -rf vendor/ install: - go install -mod=readonly $(BUILD_FLAGS) ./cmd/meshd + go install -mod=readonly $(BUILD_FLAGS) ./cmd/meshconsumerd + go install -mod=readonly $(BUILD_FLAGS) ./cmd/meshproviderd ######################################## ### Testing diff --git a/demo/app/consumer/app.go b/demo/app/consumer/app.go new file mode 100644 index 00000000..0ddec2ee --- /dev/null +++ b/demo/app/consumer/app.go @@ -0,0 +1,1076 @@ +package consumer + +import ( + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" + + "github.com/CosmWasm/wasmd/x/wasm" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/log" + tmos "github.com/cometbft/cometbft/libs/os" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + ica "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts" + icacontroller "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller" + icacontrollerkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" + icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icahost "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host" + icahostkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/keeper" + icahosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/types" + ibcfee "github.com/cosmos/ibc-go/v7/modules/apps/29-fee" + ibcfeekeeper "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/keeper" + ibcfeetypes "github.com/cosmos/ibc-go/v7/modules/apps/29-fee/types" + "github.com/cosmos/ibc-go/v7/modules/apps/transfer" + ibctransferkeeper "github.com/cosmos/ibc-go/v7/modules/apps/transfer/keeper" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v7/modules/core" + ibcclient "github.com/cosmos/ibc-go/v7/modules/core/02-client" + ibcclientclient "github.com/cosmos/ibc-go/v7/modules/core/02-client/client" + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + porttypes "github.com/cosmos/ibc-go/v7/modules/core/05-port/types" + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + ibctm "github.com/cosmos/ibc-go/v7/modules/light-clients/07-tendermint" + "github.com/spf13/cast" + + autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" + reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/store/streaming" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/posthandler" + authsims "github.com/cosmos/cosmos-sdk/x/auth/simulation" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" + "github.com/cosmos/cosmos-sdk/x/bank" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/capability" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/consensus" + consensusparamkeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" + consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + "github.com/cosmos/cosmos-sdk/x/crisis" + crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/evidence" + evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + "github.com/cosmos/cosmos-sdk/x/feegrant" + feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" + feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/gov" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/group" + groupkeeper "github.com/cosmos/cosmos-sdk/x/group/keeper" + groupmodule "github.com/cosmos/cosmos-sdk/x/group/module" + "github.com/cosmos/cosmos-sdk/x/mint" + mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/cosmos/cosmos-sdk/x/nft" + nftkeeper "github.com/cosmos/cosmos-sdk/x/nft/keeper" + nftmodule "github.com/cosmos/cosmos-sdk/x/nft/module" + "github.com/cosmos/cosmos-sdk/x/params" + paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/upgrade" + upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + demoapp "github.com/osmosis-labs/mesh-security-sdk/demo/app" + "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity" + meshseckeeper "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/keeper" + meshsectypes "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/types" +) + +const appName = "MeshConsumerApp" + +// We pull these out so we can set them with LDFLAGS in the Makefile +var ( + NodeDir = ".meshconsumerd" + Bech32Prefix = "mesh" +) + +// These constants are derived from the above variables. +// These are the ones we will want to use in the code, based on +// any overrides above +var ( + // DefaultNodeHome default home directories for wasmd + DefaultNodeHome = os.ExpandEnv("$HOME/") + NodeDir + + // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address + Bech32PrefixAccAddr = Bech32Prefix + // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key + Bech32PrefixAccPub = Bech32Prefix + sdk.PrefixPublic + // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address + Bech32PrefixValAddr = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator + // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key + Bech32PrefixValPub = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator + sdk.PrefixPublic + // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address + Bech32PrefixConsAddr = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus + // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key + Bech32PrefixConsPub = Bech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus + sdk.PrefixPublic +) + +var ( + // ModuleBasics defines the module BasicManager is in charge of setting up basic, + // non-dependant module elements, such as codec registration + // and genesis verification. + ModuleBasics = module.NewBasicManager( + auth.AppModuleBasic{}, + genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator), + bank.AppModuleBasic{}, + capability.AppModuleBasic{}, + staking.AppModuleBasic{}, + mint.AppModuleBasic{}, + distr.AppModuleBasic{}, + gov.NewAppModuleBasic( + []govclient.ProposalHandler{ + paramsclient.ProposalHandler, + upgradeclient.LegacyProposalHandler, + upgradeclient.LegacyCancelProposalHandler, + ibcclientclient.UpdateClientProposalHandler, + ibcclientclient.UpgradeProposalHandler, + }, + ), + params.AppModuleBasic{}, + crisis.AppModuleBasic{}, + slashing.AppModuleBasic{}, + feegrantmodule.AppModuleBasic{}, + upgrade.AppModuleBasic{}, + evidence.AppModuleBasic{}, + authzmodule.AppModuleBasic{}, + groupmodule.AppModuleBasic{}, + vesting.AppModuleBasic{}, + nftmodule.AppModuleBasic{}, + consensus.AppModuleBasic{}, + // non sdk modules + wasm.AppModuleBasic{}, + ibc.AppModuleBasic{}, + ibctm.AppModuleBasic{}, + transfer.AppModuleBasic{}, + ica.AppModuleBasic{}, + ibcfee.AppModuleBasic{}, + meshsecurity.AppModuleBasic{}, + ) + + // module account permissions + maccPerms = map[string][]string{ + authtypes.FeeCollectorName: nil, + distrtypes.ModuleName: nil, + minttypes.ModuleName: {authtypes.Minter}, + stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + govtypes.ModuleName: {authtypes.Burner}, + nft.ModuleName: nil, + ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + ibcfeetypes.ModuleName: nil, + icatypes.ModuleName: nil, + wasmtypes.ModuleName: {authtypes.Burner}, + meshsectypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + } +) + +var ( + _ runtime.AppI = (*MeshConsumerApp)(nil) + _ servertypes.Application = (*MeshConsumerApp)(nil) +) + +// MeshConsumerApp extended ABCI application +type MeshConsumerApp struct { + *baseapp.BaseApp + legacyAmino *codec.LegacyAmino + appCodec codec.Codec + txConfig client.TxConfig + interfaceRegistry types.InterfaceRegistry + + // keys to access the substores + keys map[string]*storetypes.KVStoreKey + tkeys map[string]*storetypes.TransientStoreKey + memKeys map[string]*storetypes.MemoryStoreKey + + // keepers + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + CapabilityKeeper *capabilitykeeper.Keeper + StakingKeeper *stakingkeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + MintKeeper mintkeeper.Keeper + DistrKeeper distrkeeper.Keeper + GovKeeper govkeeper.Keeper + CrisisKeeper *crisiskeeper.Keeper + UpgradeKeeper *upgradekeeper.Keeper + ParamsKeeper paramskeeper.Keeper + AuthzKeeper authzkeeper.Keeper + EvidenceKeeper evidencekeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper + GroupKeeper groupkeeper.Keeper + NFTKeeper nftkeeper.Keeper + ConsensusParamsKeeper consensusparamkeeper.Keeper + + IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly + IBCFeeKeeper ibcfeekeeper.Keeper + ICAControllerKeeper icacontrollerkeeper.Keeper + ICAHostKeeper icahostkeeper.Keeper + TransferKeeper ibctransferkeeper.Keeper + WasmKeeper wasmkeeper.Keeper + MeshSecKeeper *meshseckeeper.Keeper + + ScopedIBCKeeper capabilitykeeper.ScopedKeeper + ScopedICAHostKeeper capabilitykeeper.ScopedKeeper + ScopedICAControllerKeeper capabilitykeeper.ScopedKeeper + ScopedTransferKeeper capabilitykeeper.ScopedKeeper + ScopedIBCFeeKeeper capabilitykeeper.ScopedKeeper + ScopedWasmKeeper capabilitykeeper.ScopedKeeper + + // the module manager + ModuleManager *module.Manager + + // simulation manager + sm *module.SimulationManager + + // module configurator + configurator module.Configurator +} + +// NewMeshConsumerApp returns a reference to an initialized MeshConsumerApp. +func NewMeshConsumerApp( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + appOpts servertypes.AppOptions, + wasmOpts []wasmkeeper.Option, + baseAppOptions ...func(*baseapp.BaseApp), +) *MeshConsumerApp { + encodingConfig := MakeEncodingConfig() + + appCodec, legacyAmino := encodingConfig.Marshaler, encodingConfig.Amino + interfaceRegistry := encodingConfig.InterfaceRegistry + txConfig := encodingConfig.TxConfig + + bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...) + bApp.SetCommitMultiStoreTracer(traceStore) + bApp.SetVersion(version.Version) + bApp.SetInterfaceRegistry(interfaceRegistry) + bApp.SetTxEncoder(txConfig.TxEncoder()) + + keys := sdk.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, crisistypes.StoreKey, + minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, + govtypes.StoreKey, paramstypes.StoreKey, consensusparamtypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, + evidencetypes.StoreKey, capabilitytypes.StoreKey, + authzkeeper.StoreKey, nftkeeper.StoreKey, group.StoreKey, + // non sdk store keys + ibcexported.StoreKey, ibctransfertypes.StoreKey, ibcfeetypes.StoreKey, + wasmtypes.StoreKey, icahosttypes.StoreKey, + icacontrollertypes.StoreKey, + meshsectypes.StoreKey, + ) + + tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) + memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, meshsectypes.MemStoreKey) + + // load state streaming if enabled + if _, _, err := streaming.LoadStreamingServices(bApp, appOpts, appCodec, logger, keys); err != nil { + logger.Error("failed to load state streaming", "err", err) + os.Exit(1) + } + + app := &MeshConsumerApp{ + BaseApp: bApp, + legacyAmino: legacyAmino, + appCodec: appCodec, + txConfig: txConfig, + interfaceRegistry: interfaceRegistry, + keys: keys, + tkeys: tkeys, + memKeys: memKeys, + } + + app.ParamsKeeper = initParamsKeeper( + appCodec, + legacyAmino, + keys[paramstypes.StoreKey], + tkeys[paramstypes.TStoreKey], + ) + + // set the BaseApp's parameter store + app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper(appCodec, keys[consensusparamtypes.StoreKey], authtypes.NewModuleAddress(govtypes.ModuleName).String()) + bApp.SetParamStore(&app.ConsensusParamsKeeper) + + // add capability keeper and ScopeToModule for ibc module + app.CapabilityKeeper = capabilitykeeper.NewKeeper( + appCodec, + keys[capabilitytypes.StoreKey], + memKeys[capabilitytypes.MemStoreKey], + ) + + scopedIBCKeeper := app.CapabilityKeeper.ScopeToModule(ibcexported.ModuleName) + scopedICAHostKeeper := app.CapabilityKeeper.ScopeToModule(icahosttypes.SubModuleName) + scopedICAControllerKeeper := app.CapabilityKeeper.ScopeToModule(icacontrollertypes.SubModuleName) + scopedTransferKeeper := app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName) + scopedWasmKeeper := app.CapabilityKeeper.ScopeToModule(wasmtypes.ModuleName) + app.CapabilityKeeper.Seal() + + // add keepers + app.AccountKeeper = authkeeper.NewAccountKeeper( + appCodec, + keys[authtypes.StoreKey], + authtypes.ProtoBaseAccount, + maccPerms, + Bech32Prefix, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + app.BankKeeper = bankkeeper.NewBaseKeeper( + appCodec, + keys[banktypes.StoreKey], + app.AccountKeeper, + BlockedAddresses(), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + app.StakingKeeper = stakingkeeper.NewKeeper( + appCodec, + keys[stakingtypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + app.MintKeeper = mintkeeper.NewKeeper( + appCodec, + keys[minttypes.StoreKey], + app.StakingKeeper, + app.AccountKeeper, + app.BankKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + app.DistrKeeper = distrkeeper.NewKeeper( + appCodec, + keys[distrtypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + // setup mesh-security keeper with vanilla Cosmos-SDK + // see also NewKeeperX constructor for integration with Osmosis SDK fork + // should be initialized before wasm keeper for custom query/msg handlers + app.MeshSecKeeper = meshseckeeper.NewKeeper( + app.appCodec, + keys[meshsectypes.StoreKey], + memKeys[meshsectypes.MemStoreKey], + app.BankKeeper, + app.StakingKeeper, + &app.WasmKeeper, // ensure this is a pointer as we instantiate the keeper a bit later + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + app.SlashingKeeper = slashingkeeper.NewKeeper( + appCodec, + legacyAmino, + keys[slashingtypes.StoreKey], + // decorate the sdk keeper to capture all jail/ unjail events for MS + meshseckeeper.NewStakingDecorator(app.StakingKeeper, app.MeshSecKeeper), + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + invCheckPeriod := cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)) + app.CrisisKeeper = crisiskeeper.NewKeeper( + appCodec, + keys[crisistypes.StoreKey], + invCheckPeriod, + app.BankKeeper, + authtypes.FeeCollectorName, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegrant.StoreKey], app.AccountKeeper) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.StakingKeeper.SetHooks( + stakingtypes.NewMultiStakingHooks( + app.DistrKeeper.Hooks(), + app.SlashingKeeper.Hooks(), + // register hook to capture valset updates + app.MeshSecKeeper.Hooks(), + ), + ) + + app.AuthzKeeper = authzkeeper.NewKeeper(keys[authzkeeper.StoreKey], appCodec, app.MsgServiceRouter(), app.AccountKeeper) + + groupConfig := group.DefaultConfig() + /* + Example of setting group params: + groupConfig.MaxMetadataLen = 1000 + */ + app.GroupKeeper = groupkeeper.NewKeeper(keys[group.StoreKey], appCodec, app.MsgServiceRouter(), app.AccountKeeper, groupConfig) + + // get skipUpgradeHeights from the app options + skipUpgradeHeights := map[int64]bool{} + for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { + skipUpgradeHeights[int64(h)] = true + } + homePath := cast.ToString(appOpts.Get(flags.FlagHome)) + // set the governance module account as the authority for conducting upgrades + app.UpgradeKeeper = upgradekeeper.NewKeeper( + skipUpgradeHeights, + keys[upgradetypes.StoreKey], + appCodec, + homePath, + app.BaseApp, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + app.IBCKeeper = ibckeeper.NewKeeper( + appCodec, + keys[ibcexported.StoreKey], + app.GetSubspace(ibcexported.ModuleName), + app.StakingKeeper, + app.UpgradeKeeper, + scopedIBCKeeper, + ) + + // Register the proposal types + // Deprecated: Avoid adding new handlers, instead use the new proposal flow + // by granting the governance module the right to execute the message. + // See: https://docs.cosmos.network/main/modules/gov#proposal-messages + govRouter := govv1beta1.NewRouter() + govRouter.AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler). + AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). // This should be removed. It is still in place to avoid failures of modules that have not yet been upgraded. + AddRoute(upgradetypes.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)). + AddRoute(ibcclienttypes.RouterKey, ibcclient.NewClientProposalHandler(app.IBCKeeper.ClientKeeper)) + + govConfig := govtypes.DefaultConfig() + /* + Example of setting gov params: + govConfig.MaxMetadataLen = 10000 + */ + govKeeper := govkeeper.NewKeeper( + appCodec, + keys[govtypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + app.MsgServiceRouter(), + govConfig, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + + app.GovKeeper = *govKeeper.SetHooks( + govtypes.NewMultiGovHooks( + // register the governance hooks + ), + ) + + app.NFTKeeper = nftkeeper.NewKeeper( + keys[nftkeeper.StoreKey], + appCodec, + app.AccountKeeper, + app.BankKeeper, + ) + + // create evidence keeper with router + evidenceKeeper := evidencekeeper.NewKeeper( + appCodec, + keys[evidencetypes.StoreKey], + app.StakingKeeper, + // decorate the SlashingKeeper to capture the tombstone event + meshseckeeper.CaptureTombstoneDecorator(app.MeshSecKeeper, app.SlashingKeeper, app.StakingKeeper), + ) + // If evidence needs to be handled for the app, set routes in router here and seal + app.EvidenceKeeper = *evidenceKeeper + + // IBC Fee Module keeper + app.IBCFeeKeeper = ibcfeekeeper.NewKeeper( + appCodec, keys[ibcfeetypes.StoreKey], + app.IBCKeeper.ChannelKeeper, // may be replaced with IBC middleware + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, app.AccountKeeper, app.BankKeeper, + ) + + // Create Transfer Keepers + app.TransferKeeper = ibctransferkeeper.NewKeeper( + appCodec, + keys[ibctransfertypes.StoreKey], + app.GetSubspace(ibctransfertypes.ModuleName), + app.IBCFeeKeeper, // ISC4 Wrapper: fee IBC middleware + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, + app.AccountKeeper, + app.BankKeeper, + scopedTransferKeeper, + ) + + app.ICAHostKeeper = icahostkeeper.NewKeeper( + appCodec, + keys[icahosttypes.StoreKey], + app.GetSubspace(icahosttypes.SubModuleName), + app.IBCFeeKeeper, // use ics29 fee as ics4Wrapper in middleware stack + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, + app.AccountKeeper, + scopedICAHostKeeper, + app.MsgServiceRouter(), + ) + app.ICAControllerKeeper = icacontrollerkeeper.NewKeeper( + appCodec, + keys[icacontrollertypes.StoreKey], + app.GetSubspace(icacontrollertypes.SubModuleName), + app.IBCFeeKeeper, // use ics29 fee as ics4Wrapper in middleware stack + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, + scopedICAControllerKeeper, + app.MsgServiceRouter(), + ) + + wasmDir := filepath.Join(homePath, "wasm") + wasmConfig, err := wasm.ReadWasmConfig(appOpts) + if err != nil { + panic(fmt.Sprintf("error while reading wasm config: %s", err)) + } + + meshMessageHandler := wasmkeeper.WithMessageHandlerDecorator(func(nested wasmkeeper.Messenger) wasmkeeper.Messenger { + return wasmkeeper.NewMessageHandlerChain( + // security layer for system integrity, should always be first in chain + meshseckeeper.NewIntegrityHandler(app.MeshSecKeeper), + nested, + // append our custom message handler for mesh-security + meshseckeeper.NewDefaultCustomMsgHandler(app.MeshSecKeeper), + ) + }) + wasmOpts = append(wasmOpts, meshMessageHandler, + // add support for the mesh-security queries + wasmkeeper.WithQueryHandlerDecorator(meshseckeeper.NewQueryDecorator(app.MeshSecKeeper, app.StakingKeeper, app.SlashingKeeper)), + ) + // The last arguments can contain custom message handlers, and custom query handlers, + // if we want to allow any custom callbacks + availableCapabilities := strings.Join(demoapp.AllCapabilities(), ",") + app.WasmKeeper = wasmkeeper.NewKeeper( + appCodec, + keys[wasmtypes.StoreKey], + app.AccountKeeper, + app.BankKeeper, + app.StakingKeeper, + distrkeeper.NewQuerier(app.DistrKeeper), + app.IBCFeeKeeper, // ISC4 Wrapper: fee IBC middleware + app.IBCKeeper.ChannelKeeper, + &app.IBCKeeper.PortKeeper, + scopedWasmKeeper, + app.TransferKeeper, + app.MsgServiceRouter(), + app.GRPCQueryRouter(), + wasmDir, + wasmConfig, + availableCapabilities, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + wasmOpts..., + ) + + // Set legacy router for backwards compatibility with gov v1beta1 + app.GovKeeper.SetLegacyRouter(govRouter) + + // Create Transfer Stack + var transferStack porttypes.IBCModule + transferStack = transfer.NewIBCModule(app.TransferKeeper) + transferStack = ibcfee.NewIBCMiddleware(transferStack, app.IBCFeeKeeper) + + // Create Interchain Accounts Stack + // SendPacket, since it is originating from the application to core IBC: + // icaAuthModuleKeeper.SendTx -> icaController.SendPacket -> fee.SendPacket -> channel.SendPacket + var icaControllerStack porttypes.IBCModule + // integration point for custom authentication modules + // see https://medium.com/the-interchain-foundation/ibc-go-v6-changes-to-interchain-accounts-and-how-it-impacts-your-chain-806c185300d7 + var noAuthzModule porttypes.IBCModule + icaControllerStack = icacontroller.NewIBCMiddleware(noAuthzModule, app.ICAControllerKeeper) + icaControllerStack = ibcfee.NewIBCMiddleware(icaControllerStack, app.IBCFeeKeeper) + + // RecvPacket, message that originates from core IBC and goes down to app, the flow is: + // channel.RecvPacket -> fee.OnRecvPacket -> icaHost.OnRecvPacket + var icaHostStack porttypes.IBCModule + icaHostStack = icahost.NewIBCModule(app.ICAHostKeeper) + icaHostStack = ibcfee.NewIBCMiddleware(icaHostStack, app.IBCFeeKeeper) + + // Create fee enabled wasm ibc Stack + var wasmStack porttypes.IBCModule + wasmStack = wasm.NewIBCHandler(app.WasmKeeper, app.IBCKeeper.ChannelKeeper, app.IBCFeeKeeper) + wasmStack = ibcfee.NewIBCMiddleware(wasmStack, app.IBCFeeKeeper) + + // Create static IBC router, add app routes, then set and seal it + ibcRouter := porttypes.NewRouter(). + AddRoute(ibctransfertypes.ModuleName, transferStack). + AddRoute(wasmtypes.ModuleName, wasmStack). + AddRoute(icacontrollertypes.SubModuleName, icaControllerStack). + AddRoute(icahosttypes.SubModuleName, icaHostStack) + app.IBCKeeper.SetRouter(ibcRouter) + + /**** Module Options ****/ + + // NOTE: we may consider parsing `appOpts` inside module constructors. For the moment + // we prefer to be more strict in what arguments the modules expect. + skipGenesisInvariants := cast.ToBool(appOpts.Get(crisis.FlagSkipGenesisInvariants)) + + // NOTE: Any module instantiated in the module manager that is later modified + // must be passed by reference here. + app.ModuleManager = module.NewManager( + genutil.NewAppModule( + app.AccountKeeper, + app.StakingKeeper, + app.BaseApp.DeliverTx, + encodingConfig.TxConfig, + ), + auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), + bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, app.GetSubspace(banktypes.ModuleName)), + capability.NewAppModule(appCodec, *app.CapabilityKeeper, false), + feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(govtypes.ModuleName)), + mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, app.GetSubspace(minttypes.ModuleName)), + slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(slashingtypes.ModuleName)), + distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GetSubspace(distrtypes.ModuleName)), + staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName)), + upgrade.NewAppModule(app.UpgradeKeeper), + evidence.NewAppModule(app.EvidenceKeeper), + params.NewAppModule(app.ParamsKeeper), + authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + groupmodule.NewAppModule(appCodec, app.GroupKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + nftmodule.NewAppModule(appCodec, app.NFTKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper), + wasm.NewAppModule(appCodec, &app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.MsgServiceRouter(), app.GetSubspace(wasmtypes.ModuleName)), + ibc.NewAppModule(app.IBCKeeper), + transfer.NewAppModule(app.TransferKeeper), + ibcfee.NewAppModule(app.IBCFeeKeeper), + ica.NewAppModule(&app.ICAControllerKeeper, &app.ICAHostKeeper), + meshsecurity.NewAppModule(appCodec, app.MeshSecKeeper), + crisis.NewAppModule(app.CrisisKeeper, skipGenesisInvariants, app.GetSubspace(crisistypes.ModuleName)), // always be last to make sure that it checks for all invariants and not only part of them + ) + + // During begin block slashing happens after distr.BeginBlocker so that + // there is nothing left over in the validator fee pool, so as to keep the + // CanWithdrawInvariant invariant. + // NOTE: staking module is required if HistoricalEntries param > 0 + // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) + app.ModuleManager.SetOrderBeginBlockers( + upgradetypes.ModuleName, capabilitytypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, + evidencetypes.ModuleName, stakingtypes.ModuleName, + authtypes.ModuleName, banktypes.ModuleName, govtypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, + authz.ModuleName, feegrant.ModuleName, nft.ModuleName, group.ModuleName, + paramstypes.ModuleName, vestingtypes.ModuleName, consensusparamtypes.ModuleName, + // additional non simd modules + ibctransfertypes.ModuleName, + ibcexported.ModuleName, + icatypes.ModuleName, + ibcfeetypes.ModuleName, + wasmtypes.ModuleName, + meshsectypes.ModuleName, + ) + + app.ModuleManager.SetOrderEndBlockers( + crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, + capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, + slashingtypes.ModuleName, minttypes.ModuleName, + genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, + feegrant.ModuleName, nft.ModuleName, group.ModuleName, + paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, consensusparamtypes.ModuleName, + // additional non simd modules + ibctransfertypes.ModuleName, + ibcexported.ModuleName, + icatypes.ModuleName, + ibcfeetypes.ModuleName, + wasmtypes.ModuleName, + meshsectypes.ModuleName, // last to capture all chain events + ) + + // NOTE: The genutils module must occur after staking so that pools are + // properly initialized with tokens from genesis accounts. + // NOTE: The genutils module must also occur after auth so that it can access the params from auth. + // NOTE: Capability module must occur first so that it can initialize any capabilities + // so that other modules that want to create or claim capabilities afterwards in InitChain + // can do so safely. + // NOTE: wasm module should be at the end as it can call other module functionality direct or via message dispatching during + // genesis phase. For example bank transfer, auth account check, staking, ... + genesisModuleOrder := []string{ + capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, + distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, + minttypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, + feegrant.ModuleName, nft.ModuleName, group.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, + vestingtypes.ModuleName, consensusparamtypes.ModuleName, + // additional non simd modules + ibctransfertypes.ModuleName, + ibcexported.ModuleName, + icatypes.ModuleName, + ibcfeetypes.ModuleName, + // wasm after ibc transfer + wasmtypes.ModuleName, + meshsectypes.ModuleName, + } + app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...) + app.ModuleManager.SetOrderExportGenesis(genesisModuleOrder...) + + // Uncomment if you want to set a custom migration order here. + // app.ModuleManager.SetOrderMigrations(custom order) + + app.ModuleManager.RegisterInvariants(app.CrisisKeeper) + app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) + app.ModuleManager.RegisterServices(app.configurator) + + // RegisterUpgradeHandlers is used for registering any on-chain upgrades. + // Make sure it's called after `app.ModuleManager` and `app.configurator` are set. + app.RegisterUpgradeHandlers() + + autocliv1.RegisterQueryServer(app.GRPCQueryRouter(), runtimeservices.NewAutoCLIQueryService(app.ModuleManager.Modules)) + + reflectionSvc, err := runtimeservices.NewReflectionService() + if err != nil { + panic(err) + } + reflectionv1.RegisterReflectionServiceServer(app.GRPCQueryRouter(), reflectionSvc) + + // add test gRPC service for testing gRPC queries in isolation + // testdata_pulsar.RegisterQueryServer(app.GRPCQueryRouter(), testdata_pulsar.QueryImpl{}) + + // create the simulation manager and define the order of the modules for deterministic simulations + // + // NOTE: this is not required apps that don't use the simulator for fuzz testing + // transactions + overrideModules := map[string]module.AppModuleSimulation{ + authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, app.GetSubspace(authtypes.ModuleName)), + } + app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules) + + app.sm.RegisterStoreDecoders() + + // initialize stores + app.MountKVStores(keys) + app.MountTransientStores(tkeys) + app.MountMemoryStores(memKeys) + + // initialize BaseApp + app.SetInitChainer(app.InitChainer) + app.SetBeginBlocker(app.BeginBlocker) + app.SetEndBlocker(app.EndBlocker) + app.setAnteHandler(encodingConfig.TxConfig, wasmConfig, keys[wasmtypes.StoreKey]) + + // must be before Loading version + // requires the snapshot store to be created and registered as a BaseAppOption + // see cmd/wasmd/root.go: 206 - 214 approx + if manager := app.SnapshotManager(); manager != nil { + err := manager.RegisterExtensions( + wasmkeeper.NewWasmSnapshotter(app.CommitMultiStore(), &app.WasmKeeper), + ) + if err != nil { + panic(fmt.Errorf("failed to register snapshot extension: %s", err)) + } + } + + app.ScopedIBCKeeper = scopedIBCKeeper + app.ScopedTransferKeeper = scopedTransferKeeper + app.ScopedWasmKeeper = scopedWasmKeeper + app.ScopedICAHostKeeper = scopedICAHostKeeper + app.ScopedICAControllerKeeper = scopedICAControllerKeeper + + // In v0.46, the SDK introduces _postHandlers_. PostHandlers are like + // antehandlers, but are run _after_ the `runMsgs` execution. They are also + // defined as a chain, and have the same signature as antehandlers. + // + // In baseapp, postHandlers are run in the same store branch as `runMsgs`, + // meaning that both `runMsgs` and `postHandler` state will be committed if + // both are successful, and both will be reverted if any of the two fails. + // + // The SDK exposes a default postHandlers chain, which comprises of only + // one decorator: the Transaction Tips decorator. However, some chains do + // not need it by default, so feel free to comment the next line if you do + // not need tips. + // To read more about tips: + // https://docs.cosmos.network/main/core/tips.html + // + // Please note that changing any of the anteHandler or postHandler chain is + // likely to be a state-machine breaking change, which needs a coordinated + // upgrade. + app.setPostHandler() + + if loadLatest { + if err := app.LoadLatestVersion(); err != nil { + logger.Error("error on loading last version", "err", err) + os.Exit(1) + } + ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{}) + + // Initialize pinned codes in wasmvm as they are not persisted there + if err := app.WasmKeeper.InitializePinnedCodes(ctx); err != nil { + tmos.Exit(fmt.Sprintf("failed initialize pinned codes %s", err)) + } + } + + return app +} + +func (app *MeshConsumerApp) setAnteHandler(txConfig client.TxConfig, wasmConfig wasmtypes.WasmConfig, txCounterStoreKey storetypes.StoreKey) { + anteHandler, err := demoapp.NewAnteHandler( + demoapp.HandlerOptions{ + HandlerOptions: ante.HandlerOptions{ + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + SignModeHandler: txConfig.SignModeHandler(), + FeegrantKeeper: app.FeeGrantKeeper, + SigGasConsumer: ante.DefaultSigVerificationGasConsumer, + }, + IBCKeeper: app.IBCKeeper, + WasmConfig: &wasmConfig, + TXCounterStoreKey: txCounterStoreKey, + }, + ) + if err != nil { + panic(fmt.Errorf("failed to create AnteHandler: %s", err)) + } + app.SetAnteHandler(anteHandler) +} + +func (app *MeshConsumerApp) setPostHandler() { + postHandler, err := posthandler.NewPostHandler( + posthandler.HandlerOptions{}, + ) + if err != nil { + panic(err) + } + + app.SetPostHandler(postHandler) +} + +// Name returns the name of the App +func (app *MeshConsumerApp) Name() string { return app.BaseApp.Name() } + +// BeginBlocker application updates every begin block +func (app *MeshConsumerApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { + return app.ModuleManager.BeginBlock(ctx, req) +} + +// EndBlocker application updates every end block +func (app *MeshConsumerApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + return app.ModuleManager.EndBlock(ctx, req) +} + +func (app *MeshConsumerApp) Configurator() module.Configurator { + return app.configurator +} + +// InitChainer application update at chain initialization +func (app *MeshConsumerApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + var genesisState GenesisState + if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + panic(err) + } + app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()) + return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState) +} + +// LoadHeight loads a particular height +func (app *MeshConsumerApp) LoadHeight(height int64) error { + return app.LoadVersion(height) +} + +// LegacyAmino returns legacy amino codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *MeshConsumerApp) LegacyAmino() *codec.LegacyAmino { + return app.legacyAmino +} + +// AppCodec returns app codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *MeshConsumerApp) AppCodec() codec.Codec { + return app.appCodec +} + +// InterfaceRegistry returns MeshConsumerApp's InterfaceRegistry +func (app *MeshConsumerApp) InterfaceRegistry() types.InterfaceRegistry { + return app.interfaceRegistry +} + +// TxConfig returns MeshConsumerApp's TxConfig +func (app *MeshConsumerApp) TxConfig() client.TxConfig { + return app.txConfig +} + +// DefaultGenesis returns a default genesis from the registered AppModuleBasic's. +func (app *MeshConsumerApp) DefaultGenesis() map[string]json.RawMessage { + return ModuleBasics.DefaultGenesis(app.appCodec) +} + +// GetKey returns the KVStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *MeshConsumerApp) GetKey(storeKey string) *storetypes.KVStoreKey { + return app.keys[storeKey] +} + +// GetTKey returns the TransientStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *MeshConsumerApp) GetTKey(storeKey string) *storetypes.TransientStoreKey { + return app.tkeys[storeKey] +} + +// GetMemKey returns the MemStoreKey for the provided mem key. +// +// NOTE: This is solely used for testing purposes. +func (app *MeshConsumerApp) GetMemKey(storeKey string) *storetypes.MemoryStoreKey { + return app.memKeys[storeKey] +} + +// GetSubspace returns a param subspace for a given module name. +// +// NOTE: This is solely to be used for testing purposes. +func (app *MeshConsumerApp) GetSubspace(moduleName string) paramstypes.Subspace { + subspace, _ := app.ParamsKeeper.GetSubspace(moduleName) + return subspace +} + +// SimulationManager implements the SimulationApp interface +func (app *MeshConsumerApp) SimulationManager() *module.SimulationManager { + return app.sm +} + +// RegisterAPIRoutes registers all application module routes with the provided +// API server. +func (app *MeshConsumerApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { + clientCtx := apiSvr.ClientCtx + // Register new tx routes from grpc-gateway. + authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register new tendermint queries routes from grpc-gateway. + tmservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register node gRPC service for grpc-gateway. + nodeservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // Register grpc-gateway routes for all modules. + ModuleBasics.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) + + // register swagger API from root so that other applications can override easily + if err := server.RegisterSwaggerAPI(apiSvr.ClientCtx, apiSvr.Router, apiConfig.Swagger); err != nil { + panic(err) + } +} + +// RegisterTxService implements the Application.RegisterTxService method. +func (app *MeshConsumerApp) RegisterTxService(clientCtx client.Context) { + authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry) +} + +// RegisterTendermintService implements the Application.RegisterTendermintService method. +func (app *MeshConsumerApp) RegisterTendermintService(clientCtx client.Context) { + tmservice.RegisterTendermintService( + clientCtx, + app.BaseApp.GRPCQueryRouter(), + app.interfaceRegistry, + app.Query, + ) +} + +func (app *MeshConsumerApp) RegisterNodeService(clientCtx client.Context) { + nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter()) +} + +// GetMaccPerms returns a copy of the module account permissions +// +// NOTE: This is solely to be used for testing purposes. +func GetMaccPerms() map[string][]string { + dupMaccPerms := make(map[string][]string) + for k, v := range maccPerms { + dupMaccPerms[k] = v + } + + return dupMaccPerms +} + +// BlockedAddresses returns all the app's blocked account addresses. +func BlockedAddresses() map[string]bool { + modAccAddrs := make(map[string]bool) + for acc := range GetMaccPerms() { + modAccAddrs[authtypes.NewModuleAddress(acc).String()] = true + } + + // allow the following addresses to receive funds + delete(modAccAddrs, authtypes.NewModuleAddress(govtypes.ModuleName).String()) + + return modAccAddrs +} + +// initParamsKeeper init params keeper and its subspaces +func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey) paramskeeper.Keeper { + paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey) + + paramsKeeper.Subspace(authtypes.ModuleName) + paramsKeeper.Subspace(banktypes.ModuleName) + paramsKeeper.Subspace(stakingtypes.ModuleName) + paramsKeeper.Subspace(minttypes.ModuleName) + paramsKeeper.Subspace(distrtypes.ModuleName) + paramsKeeper.Subspace(slashingtypes.ModuleName) + paramsKeeper.Subspace(govtypes.ModuleName) + paramsKeeper.Subspace(crisistypes.ModuleName) + paramsKeeper.Subspace(ibctransfertypes.ModuleName) + paramsKeeper.Subspace(ibcexported.ModuleName) + paramsKeeper.Subspace(icahosttypes.SubModuleName) + paramsKeeper.Subspace(icacontrollertypes.SubModuleName) + paramsKeeper.Subspace(wasmtypes.ModuleName) + paramsKeeper.Subspace(meshsectypes.ModuleName) + + return paramsKeeper +} diff --git a/demo/app/app_test.go b/demo/app/consumer/app_test.go similarity index 80% rename from demo/app/app_test.go rename to demo/app/consumer/app_test.go index 96e0ed3c..eb365f43 100644 --- a/demo/app/app_test.go +++ b/demo/app/consumer/app_test.go @@ -1,10 +1,10 @@ -package app +package consumer import ( "os" "testing" - "github.com/CosmWasm/wasmd/x/wasm" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" dbm "github.com/cometbft/cometbft-db" "github.com/cometbft/cometbft/libs/log" "github.com/stretchr/testify/require" @@ -13,11 +13,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -var emptyWasmOpts []wasm.Option +var emptyWasmOpts []wasmkeeper.Option func TestMeshdExport(t *testing.T) { db := dbm.NewMemDB() - gapp := NewMeshAppWithCustomOptions(t, false, SetupOptions{ + gapp := NewMeshConsumerAppWithCustomOptions(t, false, SetupOptions{ Logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)), DB: db, AppOpts: simtestutil.NewAppOptionsWithFlagHome(t.TempDir()), @@ -25,7 +25,7 @@ func TestMeshdExport(t *testing.T) { gapp.Commit() // Making a new app object with the db, so that initchain hasn't been called - newGapp := NewMeshApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, simtestutil.NewAppOptionsWithFlagHome(t.TempDir()), emptyWasmOpts) + newGapp := NewMeshConsumerApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, simtestutil.NewAppOptionsWithFlagHome(t.TempDir()), emptyWasmOpts) _, err := newGapp.ExportAppStateAndValidators(false, []string{}, nil) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") } diff --git a/demo/app/encoding.go b/demo/app/consumer/encoding.go similarity index 97% rename from demo/app/encoding.go rename to demo/app/consumer/encoding.go index 8829acd7..cd090d8d 100644 --- a/demo/app/encoding.go +++ b/demo/app/consumer/encoding.go @@ -1,4 +1,4 @@ -package app +package consumer import ( "github.com/cosmos/cosmos-sdk/std" diff --git a/demo/app/export.go b/demo/app/consumer/export.go similarity index 95% rename from demo/app/export.go rename to demo/app/consumer/export.go index ef9f9295..0ffd07d9 100644 --- a/demo/app/export.go +++ b/demo/app/consumer/export.go @@ -1,4 +1,4 @@ -package app +package consumer import ( "encoding/json" @@ -16,7 +16,7 @@ import ( // ExportAppStateAndValidators exports the state of the application for a genesis // file. -func (app *MeshApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string) (servertypes.ExportedApp, error) { +func (app *MeshConsumerApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string) (servertypes.ExportedApp, error) { // as if they could withdraw from the start of the next block ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) @@ -47,7 +47,7 @@ func (app *MeshApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedA // NOTE zero height genesis is a temporary feature which will be deprecated // // in favour of export at a block height -func (app *MeshApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { +func (app *MeshConsumerApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { applyAllowedAddrs := false // check if there is a allowed address list diff --git a/demo/app/genesis.go b/demo/app/consumer/genesis.go similarity index 97% rename from demo/app/genesis.go rename to demo/app/consumer/genesis.go index 2900679c..edea50d4 100644 --- a/demo/app/genesis.go +++ b/demo/app/consumer/genesis.go @@ -1,4 +1,4 @@ -package app +package consumer import ( "encoding/json" diff --git a/demo/app/sim_test.go b/demo/app/consumer/sim_test.go similarity index 93% rename from demo/app/sim_test.go rename to demo/app/consumer/sim_test.go index 9e406699..2751f89f 100644 --- a/demo/app/sim_test.go +++ b/demo/app/consumer/sim_test.go @@ -1,4 +1,4 @@ -package app +package consumer import ( "encoding/json" @@ -130,8 +130,8 @@ func TestAppImportExport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewMeshApp(log.NewNopLogger(), newDB, nil, true, appOptions, emptyWasmOpts, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID)) - require.Equal(t, "MeshApp", newApp.Name()) + newApp := NewMeshConsumerApp(log.NewNopLogger(), newDB, nil, true, appOptions, emptyWasmOpts, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "MeshConsumerApp", newApp.Name()) var genesisState GenesisState err = json.Unmarshal(exported.AppState, &genesisState) @@ -233,8 +233,8 @@ func TestAppSimulationAfterImport(t *testing.T) { require.NoError(t, os.RemoveAll(newDir)) }() - newApp := NewMeshApp(log.NewNopLogger(), newDB, nil, true, appOptions, emptyWasmOpts, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID)) - require.Equal(t, "MeshApp", newApp.Name()) + newApp := NewMeshConsumerApp(log.NewNopLogger(), newDB, nil, true, appOptions, emptyWasmOpts, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "MeshConsumerApp", newApp.Name()) newApp.InitChain(abci.RequestInitChain{ ChainId: SimAppChainID, @@ -255,7 +255,7 @@ func TestAppSimulationAfterImport(t *testing.T) { require.NoError(t, err) } -func setupSimulationApp(t *testing.T, msg string) (simtypes.Config, dbm.DB, simtestutil.AppOptionsMap, *MeshApp) { +func setupSimulationApp(t *testing.T, msg string) (simtypes.Config, dbm.DB, simtestutil.AppOptionsMap, *MeshConsumerApp) { config := simcli.NewConfigFromFlags() config.ChainID = SimAppChainID @@ -274,8 +274,8 @@ func setupSimulationApp(t *testing.T, msg string) (simtypes.Config, dbm.DB, simt appOptions[flags.FlagHome] = dir // ensure a unique folder appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue - app := NewMeshApp(logger, db, nil, true, appOptions, emptyWasmOpts, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID)) - require.Equal(t, "MeshApp", app.Name()) + app := NewMeshConsumerApp(logger, db, nil, true, appOptions, emptyWasmOpts, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "MeshConsumerApp", app.Name()) return config, db, appOptions, app } @@ -313,7 +313,7 @@ func TestAppStateDeterminism(t *testing.T) { } db := dbm.NewMemDB() - app := NewMeshApp(logger, db, nil, true, appOptions, emptyWasmOpts, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID)) + app := NewMeshConsumerApp(logger, db, nil, true, appOptions, emptyWasmOpts, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID)) fmt.Printf( "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", diff --git a/demo/app/test_helpers.go b/demo/app/consumer/test_helpers.go similarity index 85% rename from demo/app/test_helpers.go rename to demo/app/consumer/test_helpers.go index c91bb04a..6951f211 100644 --- a/demo/app/test_helpers.go +++ b/demo/app/consumer/test_helpers.go @@ -1,4 +1,4 @@ -package app +package consumer import ( "encoding/json" @@ -45,7 +45,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -// SetupOptions defines arguments that are passed into `MeshApp` constructor. +// SetupOptions defines arguments that are passed into `MeshConsumerApp` constructor. type SetupOptions struct { Logger log.Logger DB *dbm.MemDB @@ -53,7 +53,7 @@ type SetupOptions struct { WasmOpts []wasmkeeper.Option } -func setup(t testing.TB, chainID string, withGenesis bool, invCheckPeriod uint, opts ...wasmkeeper.Option) (*MeshApp, GenesisState) { +func setup(t testing.TB, chainID string, withGenesis bool, invCheckPeriod uint, opts ...wasmkeeper.Option) (*MeshConsumerApp, GenesisState) { db := dbm.NewMemDB() nodeHome := t.TempDir() snapshotDir := filepath.Join(nodeHome, "data", "snapshots") @@ -67,15 +67,15 @@ func setup(t testing.TB, chainID string, withGenesis bool, invCheckPeriod uint, appOptions := make(simtestutil.AppOptionsMap, 0) appOptions[flags.FlagHome] = nodeHome // ensure unique folder appOptions[server.FlagInvCheckPeriod] = invCheckPeriod - app := NewMeshApp(log.NewNopLogger(), db, nil, true, appOptions, opts, bam.SetChainID(chainID), bam.SetSnapshot(snapshotStore, snapshottypes.SnapshotOptions{KeepRecent: 2})) + app := NewMeshConsumerApp(log.NewNopLogger(), db, nil, true, appOptions, opts, bam.SetChainID(chainID), bam.SetSnapshot(snapshotStore, snapshottypes.SnapshotOptions{KeepRecent: 2})) if withGenesis { return app, NewDefaultGenesisState(app.AppCodec()) } return app, GenesisState{} } -// NewMeshAppWithCustomOptions initializes a new MeshApp with custom options. -func NewMeshAppWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOptions) *MeshApp { +// NewMeshConsumerAppWithCustomOptions initializes a new MeshConsumerApp with custom options. +func NewMeshConsumerAppWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOptions) *MeshConsumerApp { t.Helper() privVal := mock.NewPV() @@ -93,7 +93,7 @@ func NewMeshAppWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOpti Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), } - app := NewMeshApp(options.Logger, options.DB, nil, true, options.AppOpts, options.WasmOpts) + app := NewMeshConsumerApp(options.Logger, options.DB, nil, true, options.AppOpts, options.WasmOpts) genesisState := NewDefaultGenesisState(app.appCodec) genesisState, err = GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) require.NoError(t, err) @@ -116,8 +116,8 @@ func NewMeshAppWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOpti return app } -// Setup initializes a new MeshApp. A Nop logger is set in MeshApp. -func Setup(t *testing.T, opts ...wasmkeeper.Option) *MeshApp { +// Setup initializes a new MeshConsumerApp. A Nop logger is set in MeshConsumerApp. +func Setup(t *testing.T, opts ...wasmkeeper.Option) *MeshConsumerApp { t.Helper() privVal := mock.NewPV() @@ -141,11 +141,11 @@ func Setup(t *testing.T, opts ...wasmkeeper.Option) *MeshApp { return app } -// SetupWithGenesisValSet initializes a new MeshApp with a validator set and genesis accounts +// SetupWithGenesisValSet initializes a new MeshConsumerApp with a validator set and genesis accounts // that also act as delegators. For simplicity, each validator is bonded with a delegation -// of one consensus engine unit in the default token of the MeshApp from first genesis -// account. A Nop logger is set in MeshApp. -func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, opts []wasmkeeper.Option, balances ...banktypes.Balance) *MeshApp { +// of one consensus engine unit in the default token of the MeshConsumerApp from first genesis +// account. A Nop logger is set in MeshConsumerApp. +func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, opts []wasmkeeper.Option, balances ...banktypes.Balance) *MeshConsumerApp { t.Helper() app, genesisState := setup(t, chainID, true, 5, opts...) @@ -195,14 +195,14 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs } // SetupWithEmptyStore set up a wasmd app instance with empty DB -func SetupWithEmptyStore(t testing.TB) *MeshApp { +func SetupWithEmptyStore(t testing.TB) *MeshConsumerApp { app, _ := setup(t, "testing", false, 0) return app } // GenesisStateWithSingleValidator initializes GenesisState with a single validator and genesis accounts // that also act as delegators. -func GenesisStateWithSingleValidator(t *testing.T, app *MeshApp) GenesisState { +func GenesisStateWithSingleValidator(t *testing.T, app *MeshConsumerApp) GenesisState { t.Helper() privVal := mock.NewPV() @@ -232,11 +232,11 @@ func GenesisStateWithSingleValidator(t *testing.T, app *MeshApp) GenesisState { // AddTestAddrsIncremental constructs and returns accNum amount of accounts with an // initial balance of accAmt in random order -func AddTestAddrsIncremental(app *MeshApp, ctx sdk.Context, accNum int, accAmt math.Int) []sdk.AccAddress { +func AddTestAddrsIncremental(app *MeshConsumerApp, ctx sdk.Context, accNum int, accAmt math.Int) []sdk.AccAddress { return addTestAddrs(app, ctx, accNum, accAmt, simtestutil.CreateIncrementalAccounts) } -func addTestAddrs(app *MeshApp, ctx sdk.Context, accNum int, accAmt math.Int, strategy simtestutil.GenerateAccountStrategy) []sdk.AccAddress { +func addTestAddrs(app *MeshConsumerApp, ctx sdk.Context, accNum int, accAmt math.Int, strategy simtestutil.GenerateAccountStrategy) []sdk.AccAddress { testAddrs := strategy(accNum) initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) @@ -248,7 +248,7 @@ func addTestAddrs(app *MeshApp, ctx sdk.Context, accNum int, accAmt math.Int, st return testAddrs } -func initAccountWithCoins(app *MeshApp, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { +func initAccountWithCoins(app *MeshConsumerApp, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, coins) if err != nil { panic(err) @@ -262,14 +262,14 @@ func initAccountWithCoins(app *MeshApp, ctx sdk.Context, addr sdk.AccAddress, co // ModuleAccountAddrs provides a list of blocked module accounts from configuration in AppConfig // -// Ported from MeshApp +// Ported from MeshConsumerApp func ModuleAccountAddrs() map[string]bool { return BlockedAddresses() } var emptyWasmOptions []wasmkeeper.Option -// NewTestNetworkFixture returns a new MeshApp AppConstructor for network simulation tests +// NewTestNetworkFixture returns a new MeshConsumerApp AppConstructor for network simulation tests func NewTestNetworkFixture() network.TestFixture { dir, err := os.MkdirTemp("", "simapp") if err != nil { @@ -277,9 +277,9 @@ func NewTestNetworkFixture() network.TestFixture { } defer os.RemoveAll(dir) - app := NewMeshApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(dir), emptyWasmOptions) + app := NewMeshConsumerApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(dir), emptyWasmOptions) appCtr := func(val network.ValidatorI) servertypes.Application { - return NewMeshApp(val.GetCtx().Logger, dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(val.GetCtx().Config.RootDir), emptyWasmOptions, bam.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), bam.SetMinGasPrices(val.GetAppConfig().MinGasPrices), bam.SetChainID(val.GetCtx().Viper.GetString(flags.FlagChainID))) + return NewMeshConsumerApp(val.GetCtx().Logger, dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(val.GetCtx().Config.RootDir), emptyWasmOptions, bam.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), bam.SetMinGasPrices(val.GetAppConfig().MinGasPrices), bam.SetChainID(val.GetCtx().Viper.GetString(flags.FlagChainID))) } return network.TestFixture{ diff --git a/demo/app/test_support.go b/demo/app/consumer/test_support.go similarity index 54% rename from demo/app/test_support.go rename to demo/app/consumer/test_support.go index 873c8c0a..e63953d8 100644 --- a/demo/app/test_support.go +++ b/demo/app/consumer/test_support.go @@ -1,4 +1,4 @@ -package app +package consumer import ( wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" @@ -11,30 +11,30 @@ import ( stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ) -func (app *MeshApp) GetIBCKeeper() *ibckeeper.Keeper { +func (app *MeshConsumerApp) GetIBCKeeper() *ibckeeper.Keeper { return app.IBCKeeper } -func (app *MeshApp) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { +func (app *MeshConsumerApp) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { return app.ScopedIBCKeeper } -func (app *MeshApp) GetBaseApp() *baseapp.BaseApp { +func (app *MeshConsumerApp) GetBaseApp() *baseapp.BaseApp { return app.BaseApp } -func (app *MeshApp) GetBankKeeper() bankkeeper.Keeper { +func (app *MeshConsumerApp) GetBankKeeper() bankkeeper.Keeper { return app.BankKeeper } -func (app *MeshApp) GetStakingKeeper() *stakingkeeper.Keeper { +func (app *MeshConsumerApp) GetStakingKeeper() *stakingkeeper.Keeper { return app.StakingKeeper } -func (app *MeshApp) GetAccountKeeper() authkeeper.AccountKeeper { +func (app *MeshConsumerApp) GetAccountKeeper() authkeeper.AccountKeeper { return app.AccountKeeper } -func (app *MeshApp) GetWasmKeeper() wasmkeeper.Keeper { +func (app *MeshConsumerApp) GetWasmKeeper() wasmkeeper.Keeper { return app.WasmKeeper } diff --git a/demo/app/upgrades.go b/demo/app/consumer/upgrades.go similarity index 98% rename from demo/app/upgrades.go rename to demo/app/consumer/upgrades.go index 3bfb7dd7..3a9257dd 100644 --- a/demo/app/upgrades.go +++ b/demo/app/consumer/upgrades.go @@ -1,4 +1,4 @@ -package app +package consumer import ( wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" @@ -32,7 +32,7 @@ import ( // v0.46.x to v0.47.x. const UpgradeName = "v046-to-v047" -func (app MeshApp) RegisterUpgradeHandlers() { +func (app MeshConsumerApp) RegisterUpgradeHandlers() { // Set param key table for params module migration for _, subspace := range app.ParamsKeeper.GetSubspaces() { subspace := subspace diff --git a/demo/app/app.go b/demo/app/provider/app.go similarity index 94% rename from demo/app/app.go rename to demo/app/provider/app.go index 03e91a0a..2412ec29 100644 --- a/demo/app/app.go +++ b/demo/app/provider/app.go @@ -1,4 +1,4 @@ -package app +package consumer import ( "encoding/json" @@ -126,6 +126,7 @@ import ( upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + demoapp "github.com/osmosis-labs/mesh-security-sdk/demo/app" "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity" meshseckeeper "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/keeper" meshsectypes "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/types" @@ -134,11 +135,11 @@ import ( meshsecprovtypes "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurityprovider/types" ) -const appName = "MeshApp" +const appName = "MeshProviderApp" // We pull these out so we can set them with LDFLAGS in the Makefile var ( - NodeDir = ".meshd" + NodeDir = ".meshproviderd" Bech32Prefix = "mesh" ) @@ -224,12 +225,12 @@ var ( ) var ( - _ runtime.AppI = (*MeshApp)(nil) - _ servertypes.Application = (*MeshApp)(nil) + _ runtime.AppI = (*MeshProviderApp)(nil) + _ servertypes.Application = (*MeshProviderApp)(nil) ) -// MeshApp extended ABCI application -type MeshApp struct { +// MeshProviderApp extended ABCI application +type MeshProviderApp struct { *baseapp.BaseApp legacyAmino *codec.LegacyAmino appCodec codec.Codec @@ -286,8 +287,8 @@ type MeshApp struct { configurator module.Configurator } -// NewMeshApp returns a reference to an initialized MeshApp. -func NewMeshApp( +// NewMeshProviderApp returns a reference to an initialized MeshProviderApp. +func NewMeshProviderApp( logger log.Logger, db dbm.DB, traceStore io.Writer, @@ -295,7 +296,7 @@ func NewMeshApp( appOpts servertypes.AppOptions, wasmOpts []wasmkeeper.Option, baseAppOptions ...func(*baseapp.BaseApp), -) *MeshApp { +) *MeshProviderApp { encodingConfig := MakeEncodingConfig() appCodec, legacyAmino := encodingConfig.Marshaler, encodingConfig.Amino @@ -331,7 +332,7 @@ func NewMeshApp( os.Exit(1) } - app := &MeshApp{ + app := &MeshProviderApp{ BaseApp: bApp, legacyAmino: legacyAmino, appCodec: appCodec, @@ -430,6 +431,7 @@ func NewMeshApp( app.BankKeeper, &app.WasmKeeper, app.StakingKeeper, + app.MeshSecKeeper, ) app.SlashingKeeper = slashingkeeper.NewKeeper( @@ -437,7 +439,7 @@ func NewMeshApp( legacyAmino, keys[slashingtypes.StoreKey], // decorate the sdk keeper to capture all jail/ unjail events for MS - meshseckeeper.NewStakingDecorator(app.StakingKeeper, app.MeshSecKeeper), + meshsecprovkeeper.NewStakingDecorator(app.StakingKeeper, app.MeshSecProvKeeper), authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) @@ -613,7 +615,7 @@ func NewMeshApp( ) // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks - availableCapabilities := strings.Join(AllCapabilities(), ",") + availableCapabilities := strings.Join(demoapp.AllCapabilities(), ",") app.WasmKeeper = wasmkeeper.NewKeeper( appCodec, keys[wasmtypes.StoreKey], @@ -877,9 +879,9 @@ func NewMeshApp( return app } -func (app *MeshApp) setAnteHandler(txConfig client.TxConfig, wasmConfig wasmtypes.WasmConfig, txCounterStoreKey storetypes.StoreKey) { - anteHandler, err := NewAnteHandler( - HandlerOptions{ +func (app *MeshProviderApp) setAnteHandler(txConfig client.TxConfig, wasmConfig wasmtypes.WasmConfig, txCounterStoreKey storetypes.StoreKey) { + anteHandler, err := demoapp.NewAnteHandler( + demoapp.HandlerOptions{ HandlerOptions: ante.HandlerOptions{ AccountKeeper: app.AccountKeeper, BankKeeper: app.BankKeeper, @@ -898,7 +900,7 @@ func (app *MeshApp) setAnteHandler(txConfig client.TxConfig, wasmConfig wasmtype app.SetAnteHandler(anteHandler) } -func (app *MeshApp) setPostHandler() { +func (app *MeshProviderApp) setPostHandler() { postHandler, err := posthandler.NewPostHandler( posthandler.HandlerOptions{}, ) @@ -910,24 +912,24 @@ func (app *MeshApp) setPostHandler() { } // Name returns the name of the App -func (app *MeshApp) Name() string { return app.BaseApp.Name() } +func (app *MeshProviderApp) Name() string { return app.BaseApp.Name() } // BeginBlocker application updates every begin block -func (app *MeshApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { +func (app *MeshProviderApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { return app.ModuleManager.BeginBlock(ctx, req) } // EndBlocker application updates every end block -func (app *MeshApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { +func (app *MeshProviderApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { return app.ModuleManager.EndBlock(ctx, req) } -func (app *MeshApp) Configurator() module.Configurator { +func (app *MeshProviderApp) Configurator() module.Configurator { return app.configurator } // InitChainer application update at chain initialization -func (app *MeshApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { +func (app *MeshProviderApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { var genesisState GenesisState if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil { panic(err) @@ -937,7 +939,7 @@ func (app *MeshApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci } // LoadHeight loads a particular height -func (app *MeshApp) LoadHeight(height int64) error { +func (app *MeshProviderApp) LoadHeight(height int64) error { return app.LoadVersion(height) } @@ -945,7 +947,7 @@ func (app *MeshApp) LoadHeight(height int64) error { // // NOTE: This is solely to be used for testing purposes as it may be desirable // for modules to register their own custom testing types. -func (app *MeshApp) LegacyAmino() *codec.LegacyAmino { +func (app *MeshProviderApp) LegacyAmino() *codec.LegacyAmino { return app.legacyAmino } @@ -953,62 +955,62 @@ func (app *MeshApp) LegacyAmino() *codec.LegacyAmino { // // NOTE: This is solely to be used for testing purposes as it may be desirable // for modules to register their own custom testing types. -func (app *MeshApp) AppCodec() codec.Codec { +func (app *MeshProviderApp) AppCodec() codec.Codec { return app.appCodec } -// InterfaceRegistry returns MeshApp's InterfaceRegistry -func (app *MeshApp) InterfaceRegistry() types.InterfaceRegistry { +// InterfaceRegistry returns MeshProviderApp's InterfaceRegistry +func (app *MeshProviderApp) InterfaceRegistry() types.InterfaceRegistry { return app.interfaceRegistry } -// TxConfig returns MeshApp's TxConfig -func (app *MeshApp) TxConfig() client.TxConfig { +// TxConfig returns MeshProviderApp's TxConfig +func (app *MeshProviderApp) TxConfig() client.TxConfig { return app.txConfig } // DefaultGenesis returns a default genesis from the registered AppModuleBasic's. -func (app *MeshApp) DefaultGenesis() map[string]json.RawMessage { +func (app *MeshProviderApp) DefaultGenesis() map[string]json.RawMessage { return ModuleBasics.DefaultGenesis(app.appCodec) } // GetKey returns the KVStoreKey for the provided store key. // // NOTE: This is solely to be used for testing purposes. -func (app *MeshApp) GetKey(storeKey string) *storetypes.KVStoreKey { +func (app *MeshProviderApp) GetKey(storeKey string) *storetypes.KVStoreKey { return app.keys[storeKey] } // GetTKey returns the TransientStoreKey for the provided store key. // // NOTE: This is solely to be used for testing purposes. -func (app *MeshApp) GetTKey(storeKey string) *storetypes.TransientStoreKey { +func (app *MeshProviderApp) GetTKey(storeKey string) *storetypes.TransientStoreKey { return app.tkeys[storeKey] } // GetMemKey returns the MemStoreKey for the provided mem key. // // NOTE: This is solely used for testing purposes. -func (app *MeshApp) GetMemKey(storeKey string) *storetypes.MemoryStoreKey { +func (app *MeshProviderApp) GetMemKey(storeKey string) *storetypes.MemoryStoreKey { return app.memKeys[storeKey] } // GetSubspace returns a param subspace for a given module name. // // NOTE: This is solely to be used for testing purposes. -func (app *MeshApp) GetSubspace(moduleName string) paramstypes.Subspace { +func (app *MeshProviderApp) GetSubspace(moduleName string) paramstypes.Subspace { subspace, _ := app.ParamsKeeper.GetSubspace(moduleName) return subspace } // SimulationManager implements the SimulationApp interface -func (app *MeshApp) SimulationManager() *module.SimulationManager { +func (app *MeshProviderApp) SimulationManager() *module.SimulationManager { return app.sm } // RegisterAPIRoutes registers all application module routes with the provided // API server. -func (app *MeshApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { +func (app *MeshProviderApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) { clientCtx := apiSvr.ClientCtx // Register new tx routes from grpc-gateway. authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter) @@ -1029,12 +1031,12 @@ func (app *MeshApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APICo } // RegisterTxService implements the Application.RegisterTxService method. -func (app *MeshApp) RegisterTxService(clientCtx client.Context) { +func (app *MeshProviderApp) RegisterTxService(clientCtx client.Context) { authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry) } // RegisterTendermintService implements the Application.RegisterTendermintService method. -func (app *MeshApp) RegisterTendermintService(clientCtx client.Context) { +func (app *MeshProviderApp) RegisterTendermintService(clientCtx client.Context) { tmservice.RegisterTendermintService( clientCtx, app.BaseApp.GRPCQueryRouter(), @@ -1043,7 +1045,7 @@ func (app *MeshApp) RegisterTendermintService(clientCtx client.Context) { ) } -func (app *MeshApp) RegisterNodeService(clientCtx client.Context) { +func (app *MeshProviderApp) RegisterNodeService(clientCtx client.Context) { nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter()) } diff --git a/demo/app/provider/app_test.go b/demo/app/provider/app_test.go new file mode 100644 index 00000000..7a73037f --- /dev/null +++ b/demo/app/provider/app_test.go @@ -0,0 +1,53 @@ +package consumer + +import ( + "os" + "testing" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + dbm "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + "github.com/stretchr/testify/require" + + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var emptyWasmOpts []wasmkeeper.Option + +func TestMeshdExport(t *testing.T) { + db := dbm.NewMemDB() + gapp := NewMeshProviderAppWithCustomOptions(t, false, SetupOptions{ + Logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)), + DB: db, + AppOpts: simtestutil.NewAppOptionsWithFlagHome(t.TempDir()), + }) + gapp.Commit() + + // Making a new app object with the db, so that initchain hasn't been called + newGapp := NewMeshProviderApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, simtestutil.NewAppOptionsWithFlagHome(t.TempDir()), emptyWasmOpts) + _, err := newGapp.ExportAppStateAndValidators(false, []string{}, nil) + require.NoError(t, err, "ExportAppStateAndValidators should not have an error") +} + +// ensure that blocked addresses are properly set in bank keeper +func TestBlockedAddrs(t *testing.T) { + gapp := Setup(t) + + for acc := range BlockedAddresses() { + t.Run(acc, func(t *testing.T) { + var addr sdk.AccAddress + if modAddr, err := sdk.AccAddressFromBech32(acc); err == nil { + addr = modAddr + } else { + addr = gapp.AccountKeeper.GetModuleAddress(acc) + } + require.True(t, gapp.BankKeeper.BlockedAddr(addr), "ensure that blocked addresses are properly set in bank keeper") + }) + } +} + +func TestGetMaccPerms(t *testing.T) { + dup := GetMaccPerms() + require.Equal(t, maccPerms, dup, "duplicated module account permissions differed from actual module account permissions") +} diff --git a/demo/app/provider/encoding.go b/demo/app/provider/encoding.go new file mode 100644 index 00000000..cd090d8d --- /dev/null +++ b/demo/app/provider/encoding.go @@ -0,0 +1,17 @@ +package consumer + +import ( + "github.com/cosmos/cosmos-sdk/std" + + "github.com/osmosis-labs/mesh-security-sdk/demo/app/params" +) + +// MakeEncodingConfig creates a new EncodingConfig with all modules registered +func MakeEncodingConfig() params.EncodingConfig { + encodingConfig := params.MakeEncodingConfig() + std.RegisterLegacyAminoCodec(encodingConfig.Amino) + std.RegisterInterfaces(encodingConfig.InterfaceRegistry) + ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino) + ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry) + return encodingConfig +} diff --git a/demo/app/provider/export.go b/demo/app/provider/export.go new file mode 100644 index 00000000..0b6a4aea --- /dev/null +++ b/demo/app/provider/export.go @@ -0,0 +1,203 @@ +package consumer + +import ( + "encoding/json" + "fmt" + "log" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + + servertypes "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// ExportAppStateAndValidators exports the state of the application for a genesis +// file. +func (app *MeshProviderApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string) (servertypes.ExportedApp, error) { + // as if they could withdraw from the start of the next block + ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + + // We export at last height + 1, because that's the height at which + // Tendermint will start InitChain. + height := app.LastBlockHeight() + 1 + if forZeroHeight { + height = 0 + app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs) + } + + genState := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport) + appState, err := json.MarshalIndent(genState, "", " ") + if err != nil { + return servertypes.ExportedApp{}, err + } + + validators, err := staking.WriteValidators(ctx, app.StakingKeeper) + return servertypes.ExportedApp{ + AppState: appState, + Validators: validators, + Height: height, + ConsensusParams: app.BaseApp.GetConsensusParams(ctx), + }, err +} + +// prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// +// in favour of export at a block height +func (app *MeshProviderApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) { + applyAllowedAddrs := false + + // check if there is a allowed address list + if len(jailAllowedAddrs) > 0 { + applyAllowedAddrs = true + } + + allowedAddrsMap := make(map[string]bool) + + for _, addr := range jailAllowedAddrs { + _, err := sdk.ValAddressFromBech32(addr) + if err != nil { + log.Fatal(err) + } + allowedAddrsMap[addr] = true + } + + /* Just to be safe, assert the invariants on current state. */ + app.CrisisKeeper.AssertInvariants(ctx) + + /* Handle fee distribution state. */ + + // withdraw all validator commission + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) + return false + }) + + // withdraw all delegator rewards + dels := app.StakingKeeper.GetAllDelegations(ctx) + for _, delegation := range dels { + valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) + if err != nil { + panic(err) + } + + delAddr := sdk.MustAccAddressFromBech32(delegation.DelegatorAddress) + + if _, err = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr); err != nil { + panic(err) + } + } + + // clear validator slash events + app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) + + // clear validator historical rewards + app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + // set context height to zero + height := ctx.BlockHeight() + ctx = ctx.WithBlockHeight(0) + + // reinitialize all validators + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { + // donate any unwithdrawn outstanding reward fraction tokens to the community pool + scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) + feePool := app.DistrKeeper.GetFeePool(ctx) + feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) + app.DistrKeeper.SetFeePool(ctx, feePool) + + if err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()); err != nil { + panic(err) + } + return false + }) + + // reinitialize all delegations + for _, del := range dels { + valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress) + if err != nil { + panic(err) + } + delAddr := sdk.MustAccAddressFromBech32(del.DelegatorAddress) + + if err := app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil { + // never called as BeforeDelegationCreated always returns nil + panic(fmt.Errorf("error while incrementing period: %w", err)) + } + + if err := app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr); err != nil { + // never called as AfterDelegationModified always returns nil + panic(fmt.Errorf("error while creating a new delegation period record: %w", err)) + } + } + + // reset context height + ctx = ctx.WithBlockHeight(height) + + /* Handle staking state. */ + + // iterate through redelegations, reset creation height + app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) { + for i := range red.Entries { + red.Entries[i].CreationHeight = 0 + } + app.StakingKeeper.SetRedelegation(ctx, red) + return false + }) + + // iterate through unbonding delegations, reset creation height + app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) { + for i := range ubd.Entries { + ubd.Entries[i].CreationHeight = 0 + } + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + return false + }) + + // Iterate through validators by power descending, reset bond heights, and + // update bond intra-tx counters. + store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey)) + iter := sdk.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey) + counter := int16(0) + + for ; iter.Valid(); iter.Next() { + addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) + validator, found := app.StakingKeeper.GetValidator(ctx, addr) + if !found { + panic("expected validator, not found") + } + + validator.UnbondingHeight = 0 + if applyAllowedAddrs && !allowedAddrsMap[addr.String()] { + validator.Jailed = true + } + + app.StakingKeeper.SetValidator(ctx, validator) + counter++ + } + + if err := iter.Close(); err != nil { + app.Logger().Error("error while closing the key-value store reverse prefix iterator: ", err) + return + } + + _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + log.Fatal(err) + } + + /* Handle slashing state. */ + + // reset start height on signing infos + app.SlashingKeeper.IterateValidatorSigningInfos( + ctx, + func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) { + info.StartHeight = 0 + app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info) + return false + }, + ) +} diff --git a/demo/app/provider/genesis.go b/demo/app/provider/genesis.go new file mode 100644 index 00000000..edea50d4 --- /dev/null +++ b/demo/app/provider/genesis.go @@ -0,0 +1,21 @@ +package consumer + +import ( + "encoding/json" + + "github.com/cosmos/cosmos-sdk/codec" +) + +// GenesisState of the blockchain is represented here as a map of raw json +// messages key'd by a identifier string. +// The identifier is used to determine which module genesis information belongs +// to so it may be appropriately routed during init chain. +// Within this application default genesis information is retrieved from +// the ModuleBasicManager which populates json from each BasicModule +// object provided to it during init. +type GenesisState map[string]json.RawMessage + +// NewDefaultGenesisState generates the default state for the application. +func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState { + return ModuleBasics.DefaultGenesis(cdc) +} diff --git a/demo/app/provider/sim_test.go b/demo/app/provider/sim_test.go new file mode 100644 index 00000000..6dbcaf15 --- /dev/null +++ b/demo/app/provider/sim_test.go @@ -0,0 +1,351 @@ +package consumer + +import ( + "encoding/json" + "fmt" + "math/rand" + "os" + "runtime/debug" + "strings" + "testing" + + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/simulation" + simcli "github.com/cosmos/cosmos-sdk/x/simulation/client/cli" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// SimAppChainID hardcoded chainID for simulation +const SimAppChainID = "simulation-app" + +// Get flags every time the simulator is run +func init() { + simcli.GetSimulatorFlags() +} + +type StoreKeysPrefixes struct { + A storetypes.StoreKey + B storetypes.StoreKey + Prefixes [][]byte +} + +// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of +// an IAVLStore for faster simulation speed. +func fauxMerkleModeOpt(bapp *baseapp.BaseApp) { + bapp.SetFauxMerkleMode() +} + +// interBlockCacheOpt returns a BaseApp option function that sets the persistent +// inter-block write-through cache. +func interBlockCacheOpt() func(*baseapp.BaseApp) { + return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) +} + +func TestFullAppSimulation(t *testing.T) { + config, db, _, app := setupSimulationApp(t, "skipping application simulation") + // run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed( + t, + os.Stdout, + app.BaseApp, + simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + simtestutil.SimulationOperations(app, app.AppCodec(), config), + BlockedAddresses(), + config, + app.AppCodec(), + ) + + // export state and simParams before the simulation error is checked + err := simtestutil.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simtestutil.PrintStats(db) + } +} + +func TestAppImportExport(t *testing.T) { + config, db, appOptions, app := setupSimulationApp(t, "skipping application import/export simulation") + + // Run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed( + t, + os.Stdout, + app.BaseApp, + simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + simtestutil.SimulationOperations(app, app.AppCodec(), config), + BlockedAddresses(), + config, + app.AppCodec(), + ) + + // export state and simParams before the simulation error is checked + err := simtestutil.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simtestutil.PrintStats(db) + } + + t.Log("exporting genesis...\n") + + exported, err := app.ExportAppStateAndValidators(false, []string{}, []string{}) + require.NoError(t, err) + + t.Log("importing genesis...\n") + + newDB, newDir, _, _, err := simtestutil.SetupSimulation(config, "leveldb-app-sim-2", "Simulation-2", simcli.FlagVerboseValue, simcli.FlagEnabledValue) + require.NoError(t, err, "simulation setup failed") + + defer func() { + require.NoError(t, newDB.Close()) + require.NoError(t, os.RemoveAll(newDir)) + }() + + newApp := NewMeshProviderApp(log.NewNopLogger(), newDB, nil, true, appOptions, emptyWasmOpts, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "MeshProviderApp", newApp.Name()) + + var genesisState GenesisState + err = json.Unmarshal(exported.AppState, &genesisState) + require.NoError(t, err) + + defer func() { + if r := recover(); r != nil { + err := fmt.Sprintf("%v", r) + if !strings.Contains(err, "validator set is empty after InitGenesis") { + panic(r) + } + t.Log("Skipping simulation as all validators have been unbonded") + t.Logf("err: %s stacktrace: %s\n", err, string(debug.Stack())) + } + }() + + ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) + newApp.ModuleManager.InitGenesis(ctxB, app.AppCodec(), genesisState) + newApp.StoreConsensusParams(ctxB, exported.ConsensusParams) + + t.Log("comparing stores...") + storeKeysPrefixes := []StoreKeysPrefixes{ + {app.GetKey(wasmtypes.StoreKey), newApp.GetKey(wasmtypes.StoreKey), [][]byte{wasmtypes.TXCounterPrefix}}, + {app.GetKey(authtypes.StoreKey), newApp.GetKey(authtypes.StoreKey), [][]byte{}}, + { + app.GetKey(stakingtypes.StoreKey), newApp.GetKey(stakingtypes.StoreKey), + [][]byte{ + stakingtypes.UnbondingQueueKey, stakingtypes.RedelegationQueueKey, stakingtypes.ValidatorQueueKey, + stakingtypes.HistoricalInfoKey, stakingtypes.UnbondingIDKey, stakingtypes.UnbondingIndexKey, stakingtypes.UnbondingTypeKey, stakingtypes.ValidatorUpdatesKey, + }, + }, // ordering may change but it doesn't matter + {app.GetKey(slashingtypes.StoreKey), newApp.GetKey(slashingtypes.StoreKey), [][]byte{}}, + {app.GetKey(minttypes.StoreKey), newApp.GetKey(minttypes.StoreKey), [][]byte{}}, + {app.GetKey(distrtypes.StoreKey), newApp.GetKey(distrtypes.StoreKey), [][]byte{}}, + {app.GetKey(banktypes.StoreKey), newApp.GetKey(banktypes.StoreKey), [][]byte{banktypes.BalancesPrefix}}, + {app.GetKey(paramtypes.StoreKey), newApp.GetKey(paramtypes.StoreKey), [][]byte{}}, + {app.GetKey(govtypes.StoreKey), newApp.GetKey(govtypes.StoreKey), [][]byte{}}, + {app.GetKey(evidencetypes.StoreKey), newApp.GetKey(evidencetypes.StoreKey), [][]byte{}}, + {app.GetKey(capabilitytypes.StoreKey), newApp.GetKey(capabilitytypes.StoreKey), [][]byte{}}, + {app.GetKey(authzkeeper.StoreKey), newApp.GetKey(authzkeeper.StoreKey), [][]byte{authzkeeper.GrantKey, authzkeeper.GrantQueuePrefix}}, + } + + for _, skp := range storeKeysPrefixes { + storeA := ctxA.KVStore(skp.A) + storeB := ctxB.KVStore(skp.B) + require.NotNil(t, storeA) + require.NotNil(t, storeB) + failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes) + require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare") + + t.Logf("compared %d different key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B) + require.Equal(t, 0, len(failedKVAs), simtestutil.GetSimulationLog(skp.A.Name(), app.SimulationManager().StoreDecoders, failedKVAs, failedKVBs)) + } +} + +func TestAppSimulationAfterImport(t *testing.T) { + config, db, appOptions, app := setupSimulationApp(t, "skipping application simulation after import") + + // Run randomized simulation + stopEarly, simParams, simErr := simulation.SimulateFromSeed( + t, + os.Stdout, + app.BaseApp, + simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + simtestutil.SimulationOperations(app, app.AppCodec(), config), + BlockedAddresses(), + config, + app.AppCodec(), + ) + + // export state and simParams before the simulation error is checked + err := simtestutil.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simtestutil.PrintStats(db) + } + + if stopEarly { + fmt.Println("can't export or import a zero-validator genesis, exiting test...") + return + } + + fmt.Printf("exporting genesis...\n") + + exported, err := app.ExportAppStateAndValidators(true, []string{}, []string{}) + require.NoError(t, err) + + fmt.Printf("importing genesis...\n") + + newDB, newDir, _, _, err := simtestutil.SetupSimulation(config, "leveldb-app-sim-2", "Simulation-2", simcli.FlagVerboseValue, simcli.FlagEnabledValue) + require.NoError(t, err, "simulation setup failed") + + defer func() { + require.NoError(t, newDB.Close()) + require.NoError(t, os.RemoveAll(newDir)) + }() + + newApp := NewMeshProviderApp(log.NewNopLogger(), newDB, nil, true, appOptions, emptyWasmOpts, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "MeshProviderApp", newApp.Name()) + + newApp.InitChain(abci.RequestInitChain{ + ChainId: SimAppChainID, + AppStateBytes: exported.AppState, + }) + + _, _, err = simulation.SimulateFromSeed( + t, + os.Stdout, + newApp.BaseApp, + simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + simtestutil.SimulationOperations(newApp, newApp.AppCodec(), config), + BlockedAddresses(), + config, + app.AppCodec(), + ) + require.NoError(t, err) +} + +func setupSimulationApp(t *testing.T, msg string) (simtypes.Config, dbm.DB, simtestutil.AppOptionsMap, *MeshProviderApp) { + config := simcli.NewConfigFromFlags() + config.ChainID = SimAppChainID + + db, dir, logger, skip, err := simtestutil.SetupSimulation(config, "leveldb-app-sim", "Simulation", simcli.FlagVerboseValue, simcli.FlagEnabledValue) + if skip { + t.Skip(msg) + } + require.NoError(t, err, "simulation setup failed") + + t.Cleanup(func() { + require.NoError(t, db.Close()) + require.NoError(t, os.RemoveAll(dir)) + }) + + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = dir // ensure a unique folder + appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue + + app := NewMeshProviderApp(logger, db, nil, true, appOptions, emptyWasmOpts, fauxMerkleModeOpt, baseapp.SetChainID(SimAppChainID)) + require.Equal(t, "MeshProviderApp", app.Name()) + return config, db, appOptions, app +} + +// TODO: Make another test for the fuzzer itself, which just has noOp txs +// and doesn't depend on the application. +func TestAppStateDeterminism(t *testing.T) { + if !simcli.FlagEnabledValue { + t.Skip("skipping application simulation") + } + + config := simcli.NewConfigFromFlags() + config.InitialBlockHeight = 1 + config.ExportParamsPath = "" + config.OnOperation = false + config.AllInvariants = false + config.ChainID = SimAppChainID + + numSeeds := 3 + numTimesToRunPerSeed := 5 + appHashList := make([]json.RawMessage, numTimesToRunPerSeed) + + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = t.TempDir() // ensure a unique folder + appOptions[server.FlagInvCheckPeriod] = simcli.FlagPeriodValue + + for i := 0; i < numSeeds; i++ { + config.Seed = rand.Int63() + + for j := 0; j < numTimesToRunPerSeed; j++ { + var logger log.Logger + if simcli.FlagVerboseValue { + logger = log.TestingLogger() + } else { + logger = log.NewNopLogger() + } + + db := dbm.NewMemDB() + app := NewMeshProviderApp(logger, db, nil, true, appOptions, emptyWasmOpts, interBlockCacheOpt(), baseapp.SetChainID(SimAppChainID)) + + fmt.Printf( + "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", + config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, + ) + + _, _, err := simulation.SimulateFromSeed( + t, + os.Stdout, + app.BaseApp, + simtestutil.AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()), + simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 + simtestutil.SimulationOperations(app, app.AppCodec(), config), + BlockedAddresses(), + config, + app.AppCodec(), + ) + require.NoError(t, err) + + if config.Commit { + simtestutil.PrintStats(db) + } + + appHash := app.LastCommitID().Hash + appHashList[j] = appHash + + if j != 0 { + require.Equal( + t, string(appHashList[0]), string(appHashList[j]), + "non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, + ) + } + } + } +} diff --git a/demo/app/provider/test_helpers.go b/demo/app/provider/test_helpers.go new file mode 100644 index 00000000..9b1830ab --- /dev/null +++ b/demo/app/provider/test_helpers.go @@ -0,0 +1,404 @@ +package consumer + +import ( + "encoding/json" + "fmt" + "math/rand" + "os" + "path/filepath" + "testing" + "time" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + tmjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmtypes "github.com/cometbft/cometbft/types" + "github.com/stretchr/testify/require" + + "cosmossdk.io/math" + + bam "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/server" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/snapshots" + snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" + pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" + "github.com/cosmos/cosmos-sdk/testutil/mock" + "github.com/cosmos/cosmos-sdk/testutil/network" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +// SetupOptions defines arguments that are passed into `MeshProviderApp` constructor. +type SetupOptions struct { + Logger log.Logger + DB *dbm.MemDB + AppOpts servertypes.AppOptions + WasmOpts []wasmkeeper.Option +} + +func setup(t testing.TB, chainID string, withGenesis bool, invCheckPeriod uint, opts ...wasmkeeper.Option) (*MeshProviderApp, GenesisState) { + db := dbm.NewMemDB() + nodeHome := t.TempDir() + snapshotDir := filepath.Join(nodeHome, "data", "snapshots") + + snapshotDB, err := dbm.NewDB("metadata", dbm.GoLevelDBBackend, snapshotDir) + require.NoError(t, err) + t.Cleanup(func() { snapshotDB.Close() }) + snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir) + require.NoError(t, err) + + appOptions := make(simtestutil.AppOptionsMap, 0) + appOptions[flags.FlagHome] = nodeHome // ensure unique folder + appOptions[server.FlagInvCheckPeriod] = invCheckPeriod + app := NewMeshProviderApp(log.NewNopLogger(), db, nil, true, appOptions, opts, bam.SetChainID(chainID), bam.SetSnapshot(snapshotStore, snapshottypes.SnapshotOptions{KeepRecent: 2})) + if withGenesis { + return app, NewDefaultGenesisState(app.AppCodec()) + } + return app, GenesisState{} +} + +// NewMeshProviderAppWithCustomOptions initializes a new MeshProviderApp with custom options. +func NewMeshProviderAppWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOptions) *MeshProviderApp { + t.Helper() + + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) + + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + } + + app := NewMeshProviderApp(options.Logger, options.DB, nil, true, options.AppOpts, options.WasmOpts) + genesisState := NewDefaultGenesisState(app.appCodec) + genesisState, err = GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balance) + require.NoError(t, err) + + if !isCheckTx { + // init chain must be called to stop deliverState from being nil + stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + + // Initialize the chain + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: simtestutil.DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) + } + + return app +} + +// Setup initializes a new MeshProviderApp. A Nop logger is set in MeshProviderApp. +func Setup(t *testing.T, opts ...wasmkeeper.Option) *MeshProviderApp { + t.Helper() + + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) + + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + } + chainID := "testing" + app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, chainID, opts, balance) + + return app +} + +// SetupWithGenesisValSet initializes a new MeshProviderApp with a validator set and genesis accounts +// that also act as delegators. For simplicity, each validator is bonded with a delegation +// of one consensus engine unit in the default token of the MeshProviderApp from first genesis +// account. A Nop logger is set in MeshProviderApp. +func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, opts []wasmkeeper.Option, balances ...banktypes.Balance) *MeshProviderApp { + t.Helper() + + app, genesisState := setup(t, chainID, true, 5, opts...) + genesisState, err := GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, genAccs, balances...) + require.NoError(t, err) + + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + + // init chain will set the validator set and initialize the genesis accounts + consensusParams := simtestutil.DefaultConsensusParams + consensusParams.Block.MaxGas = 100 * simtestutil.DefaultGenTxGas + app.InitChain( + abci.RequestInitChain{ + ChainId: chainID, + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: consensusParams, + AppStateBytes: stateBytes, + }, + ) + // commit genesis changes + app.Commit() + + votes := make([]abci.VoteInfo, len(valSet.Validators)) + for i, v := range valSet.Validators { + votes[i] = abci.VoteInfo{ + Validator: abci.Validator{Address: v.Address, Power: v.VotingPower}, + SignedLastBlock: true, + } + } + + app.BeginBlock(abci.RequestBeginBlock{ + Header: tmproto.Header{ + ChainID: chainID, + Height: app.LastBlockHeight() + 1, + AppHash: app.LastCommitID().Hash, + Time: time.Now().UTC(), + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + }, + LastCommitInfo: abci.CommitInfo{ + Votes: votes, + }, + }) + + return app +} + +// SetupWithEmptyStore set up a wasmd app instance with empty DB +func SetupWithEmptyStore(t testing.TB) *MeshProviderApp { + app, _ := setup(t, "testing", false, 0) + return app +} + +// GenesisStateWithSingleValidator initializes GenesisState with a single validator and genesis accounts +// that also act as delegators. +func GenesisStateWithSingleValidator(t *testing.T, app *MeshProviderApp) GenesisState { + t.Helper() + + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) + + // generate genesis account + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balances := []banktypes.Balance{ + { + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), + }, + } + + genesisState := NewDefaultGenesisState(app.appCodec) + genesisState, err = GenesisStateWithValSet(app.AppCodec(), genesisState, valSet, []authtypes.GenesisAccount{acc}, balances...) + require.NoError(t, err) + + return genesisState +} + +// AddTestAddrsIncremental constructs and returns accNum amount of accounts with an +// initial balance of accAmt in random order +func AddTestAddrsIncremental(app *MeshProviderApp, ctx sdk.Context, accNum int, accAmt math.Int) []sdk.AccAddress { + return addTestAddrs(app, ctx, accNum, accAmt, simtestutil.CreateIncrementalAccounts) +} + +func addTestAddrs(app *MeshProviderApp, ctx sdk.Context, accNum int, accAmt math.Int, strategy simtestutil.GenerateAccountStrategy) []sdk.AccAddress { + testAddrs := strategy(accNum) + + initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) + + for _, addr := range testAddrs { + initAccountWithCoins(app, ctx, addr, initCoins) + } + + return testAddrs +} + +func initAccountWithCoins(app *MeshProviderApp, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { + err := app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, coins) + if err != nil { + panic(err) + } + + err = app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, coins) + if err != nil { + panic(err) + } +} + +// ModuleAccountAddrs provides a list of blocked module accounts from configuration in AppConfig +// +// Ported from MeshProviderApp +func ModuleAccountAddrs() map[string]bool { + return BlockedAddresses() +} + +var emptyWasmOptions []wasmkeeper.Option + +// NewTestNetworkFixture returns a new MeshProviderApp AppConstructor for network simulation tests +func NewTestNetworkFixture() network.TestFixture { + dir, err := os.MkdirTemp("", "simapp") + if err != nil { + panic(fmt.Sprintf("failed creating temporary directory: %v", err)) + } + defer os.RemoveAll(dir) + + app := NewMeshProviderApp(log.NewNopLogger(), dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(dir), emptyWasmOptions) + appCtr := func(val network.ValidatorI) servertypes.Application { + return NewMeshProviderApp(val.GetCtx().Logger, dbm.NewMemDB(), nil, true, simtestutil.NewAppOptionsWithFlagHome(val.GetCtx().Config.RootDir), emptyWasmOptions, bam.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), bam.SetMinGasPrices(val.GetAppConfig().MinGasPrices), bam.SetChainID(val.GetCtx().Viper.GetString(flags.FlagChainID))) + } + + return network.TestFixture{ + AppConstructor: appCtr, + GenesisState: NewDefaultGenesisState(app.AppCodec()), + EncodingConfig: testutil.TestEncodingConfig{ + InterfaceRegistry: app.InterfaceRegistry(), + Codec: app.AppCodec(), + TxConfig: app.TxConfig(), + Amino: app.LegacyAmino(), + }, + } +} + +// SignAndDeliverWithoutCommit signs and delivers a transaction. No commit +func SignAndDeliverWithoutCommit( + t *testing.T, txCfg client.TxConfig, app *bam.BaseApp, msgs []sdk.Msg, fees sdk.Coins, + chainID string, accNums, accSeqs []uint64, priv ...cryptotypes.PrivKey, +) (sdk.GasInfo, *sdk.Result, error) { + tx, err := simtestutil.GenSignedMockTx( + rand.New(rand.NewSource(time.Now().UnixNano())), + txCfg, + msgs, + fees, + simtestutil.DefaultGenTxGas, + chainID, + accNums, + accSeqs, + priv..., + ) + require.NoError(t, err) + + // Simulate a sending a transaction and committing a block + // app.BeginBlock(abci.RequestBeginBlock{Header: header}) + gInfo, res, err := app.SimDeliver(txCfg.TxEncoder(), tx) + // app.EndBlock(abci.RequestEndBlock{}) + // app.Commit() + + return gInfo, res, err +} + +// GenesisStateWithValSet returns a new genesis state with the validator set +// copied from simtestutil with delegation not added to supply +func GenesisStateWithValSet( + codec codec.Codec, + genesisState map[string]json.RawMessage, + valSet *tmtypes.ValidatorSet, + genAccs []authtypes.GenesisAccount, + balances ...banktypes.Balance, +) (map[string]json.RawMessage, error) { + // set genesis accounts + authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) + genesisState[authtypes.ModuleName] = codec.MustMarshalJSON(authGenesis) + + validators := make([]stakingtypes.Validator, 0, len(valSet.Validators)) + delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators)) + + bondAmt := sdk.DefaultPowerReduction + + for _, val := range valSet.Validators { + pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) + if err != nil { + return nil, fmt.Errorf("failed to convert pubkey: %w", err) + } + + pkAny, err := codectypes.NewAnyWithValue(pk) + if err != nil { + return nil, fmt.Errorf("failed to create new any: %w", err) + } + + validator := stakingtypes.Validator{ + OperatorAddress: sdk.ValAddress(val.Address).String(), + ConsensusPubkey: pkAny, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: bondAmt, + DelegatorShares: math.LegacyOneDec(), + Description: stakingtypes.Description{}, + UnbondingHeight: int64(0), + UnbondingTime: time.Unix(0, 0).UTC(), + Commission: stakingtypes.NewCommission(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()), + MinSelfDelegation: math.ZeroInt(), + } + validators = append(validators, validator) + delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), math.LegacyOneDec())) + } + + // set validators and delegations + stakingGenesis := stakingtypes.NewGenesisState(stakingtypes.DefaultParams(), validators, delegations) + genesisState[stakingtypes.ModuleName] = codec.MustMarshalJSON(stakingGenesis) + + signingInfos := make([]slashingtypes.SigningInfo, len(valSet.Validators)) + for i, val := range valSet.Validators { + signingInfos[i] = slashingtypes.SigningInfo{ + Address: sdk.ConsAddress(val.Address).String(), + ValidatorSigningInfo: slashingtypes.ValidatorSigningInfo{}, + } + } + slashingParams := slashingtypes.DefaultParams() + slashingParams.SlashFractionDowntime = math.LegacyNewDec(1).Quo(math.LegacyNewDec(10)) + slashingParams.SlashFractionDoubleSign = math.LegacyNewDec(1).Quo(math.LegacyNewDec(10)) + slashingGenesis := slashingtypes.NewGenesisState(slashingParams, signingInfos, nil) + genesisState[slashingtypes.ModuleName] = codec.MustMarshalJSON(slashingGenesis) + + // add bonded amount to bonded pool module account + balances = append(balances, banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(), + Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt.MulRaw(int64(len(valSet.Validators))))}, + }) + + totalSupply := sdk.NewCoins() + for _, b := range balances { + // add genesis acc tokens to total supply + totalSupply = totalSupply.Add(b.Coins...) + } + + // update total supply + bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}, []banktypes.SendEnabled{}) + genesisState[banktypes.ModuleName] = codec.MustMarshalJSON(bankGenesis) + println(string(genesisState[banktypes.ModuleName])) + return genesisState, nil +} diff --git a/demo/app/provider/test_support.go b/demo/app/provider/test_support.go new file mode 100644 index 00000000..d725832c --- /dev/null +++ b/demo/app/provider/test_support.go @@ -0,0 +1,40 @@ +package consumer + +import ( + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + + "github.com/cosmos/cosmos-sdk/baseapp" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" +) + +func (app *MeshProviderApp) GetIBCKeeper() *ibckeeper.Keeper { + return app.IBCKeeper +} + +func (app *MeshProviderApp) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { + return app.ScopedIBCKeeper +} + +func (app *MeshProviderApp) GetBaseApp() *baseapp.BaseApp { + return app.BaseApp +} + +func (app *MeshProviderApp) GetBankKeeper() bankkeeper.Keeper { + return app.BankKeeper +} + +func (app *MeshProviderApp) GetStakingKeeper() *stakingkeeper.Keeper { + return app.StakingKeeper +} + +func (app *MeshProviderApp) GetAccountKeeper() authkeeper.AccountKeeper { + return app.AccountKeeper +} + +func (app *MeshProviderApp) GetWasmKeeper() wasmkeeper.Keeper { + return app.WasmKeeper +} diff --git a/demo/app/provider/upgrades.go b/demo/app/provider/upgrades.go new file mode 100644 index 00000000..251b7610 --- /dev/null +++ b/demo/app/provider/upgrades.go @@ -0,0 +1,108 @@ +package consumer + +import ( + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" + icahosttypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/host/types" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + 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" + consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +// UpgradeName defines the on-chain upgrade name for the sample SimApp upgrade +// from v046 to v047. +// +// NOTE: This upgrade defines a reference implementation of what an upgrade +// could look like when an application is migrating from Cosmos SDK version +// v0.46.x to v0.47.x. +const UpgradeName = "v046-to-v047" + +func (app MeshProviderApp) RegisterUpgradeHandlers() { + // Set param key table for params module migration + for _, subspace := range app.ParamsKeeper.GetSubspaces() { + subspace := subspace + + var keyTable paramstypes.KeyTable + switch subspace.Name() { + case authtypes.ModuleName: + keyTable = authtypes.ParamKeyTable() //nolint:staticcheck + case banktypes.ModuleName: + keyTable = banktypes.ParamKeyTable() //nolint:staticcheck + case stakingtypes.ModuleName: + keyTable = stakingtypes.ParamKeyTable() + case minttypes.ModuleName: + keyTable = minttypes.ParamKeyTable() //nolint:staticcheck + case distrtypes.ModuleName: + keyTable = distrtypes.ParamKeyTable() //nolint:staticcheck + case slashingtypes.ModuleName: + keyTable = slashingtypes.ParamKeyTable() //nolint:staticcheck + case govtypes.ModuleName: + keyTable = govv1.ParamKeyTable() //nolint:staticcheck + case crisistypes.ModuleName: + keyTable = crisistypes.ParamKeyTable() //nolint:staticcheck + // ibc types + case ibctransfertypes.ModuleName: + keyTable = ibctransfertypes.ParamKeyTable() + case icahosttypes.SubModuleName: + keyTable = icahosttypes.ParamKeyTable() + case icacontrollertypes.SubModuleName: + keyTable = icacontrollertypes.ParamKeyTable() + // wasm + case wasmtypes.ModuleName: + keyTable = wasmtypes.ParamKeyTable() //nolint:staticcheck + default: + continue + } + + if !subspace.HasKeyTable() { + subspace.WithKeyTable(keyTable) + } + } + + baseAppLegacySS := app.ParamsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramstypes.ConsensusParamsKeyTable()) + + app.UpgradeKeeper.SetUpgradeHandler( + UpgradeName, + func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // Migrate Tendermint consensus parameters from x/params module to a dedicated x/consensus module. + baseapp.MigrateParams(ctx, baseAppLegacySS, &app.ConsensusParamsKeeper) + + // Note: this migration is optional, + // You can include x/gov proposal migration documented in [UPGRADING.md](https://github.com/cosmos/cosmos-sdk/blob/main/UPGRADING.md) + + return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM) + }, + ) + + upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + if err != nil { + panic(err) + } + + if upgradeInfo.Name == UpgradeName && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { + storeUpgrades := storetypes.StoreUpgrades{ + Added: []string{ + consensustypes.ModuleName, + crisistypes.ModuleName, + }, + } + + // configure store loader that checks if version == upgradeHeight and applies store upgrades + app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades)) + } +} diff --git a/demo/build/meshconsumerd b/demo/build/meshconsumerd new file mode 100755 index 00000000..794b09f5 Binary files /dev/null and b/demo/build/meshconsumerd differ diff --git a/demo/build/meshd b/demo/build/meshproviderd similarity index 85% rename from demo/build/meshd rename to demo/build/meshproviderd index c57f7961..61a1d1d5 100755 Binary files a/demo/build/meshd and b/demo/build/meshproviderd differ diff --git a/demo/cmd/meshd/genaccount.go b/demo/cmd/genaccount.go similarity index 99% rename from demo/cmd/meshd/genaccount.go rename to demo/cmd/genaccount.go index 66690f8c..701d7a5b 100644 --- a/demo/cmd/meshd/genaccount.go +++ b/demo/cmd/genaccount.go @@ -1,4 +1,4 @@ -package main +package cmd import ( "bufio" diff --git a/demo/cmd/meshd/main.go b/demo/cmd/meshconsumerd/main.go similarity index 65% rename from demo/cmd/meshd/main.go rename to demo/cmd/meshconsumerd/main.go index c95faaca..c556031e 100644 --- a/demo/cmd/meshd/main.go +++ b/demo/cmd/meshconsumerd/main.go @@ -6,13 +6,13 @@ import ( "github.com/cosmos/cosmos-sdk/server" svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" - "github.com/osmosis-labs/mesh-security-sdk/demo/app" + "github.com/osmosis-labs/mesh-security-sdk/demo/app/consumer" ) func main() { rootCmd, _ := NewRootCmd() - if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil { + if err := svrcmd.Execute(rootCmd, "", consumer.DefaultNodeHome); err != nil { switch e := err.(type) { case server.ErrorCode: os.Exit(e.Code) diff --git a/demo/cmd/meshd/root.go b/demo/cmd/meshconsumerd/root.go similarity index 84% rename from demo/cmd/meshd/root.go rename to demo/cmd/meshconsumerd/root.go index 270afe88..37246a75 100644 --- a/demo/cmd/meshd/root.go +++ b/demo/cmd/meshconsumerd/root.go @@ -39,19 +39,20 @@ import ( genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/osmosis-labs/mesh-security-sdk/demo/app" + consumerapp "github.com/osmosis-labs/mesh-security-sdk/demo/app/consumer" "github.com/osmosis-labs/mesh-security-sdk/demo/app/params" + "github.com/osmosis-labs/mesh-security-sdk/demo/cmd" ) // NewRootCmd creates a new root command for wasmd. It is called once in the // main function. func NewRootCmd() (*cobra.Command, params.EncodingConfig) { - encodingConfig := app.MakeEncodingConfig() + encodingConfig := consumerapp.MakeEncodingConfig() cfg := sdk.GetConfig() - cfg.SetBech32PrefixForAccount(app.Bech32PrefixAccAddr, app.Bech32PrefixAccPub) - cfg.SetBech32PrefixForValidator(app.Bech32PrefixValAddr, app.Bech32PrefixValPub) - cfg.SetBech32PrefixForConsensusNode(app.Bech32PrefixConsAddr, app.Bech32PrefixConsPub) + cfg.SetBech32PrefixForAccount(consumerapp.Bech32PrefixAccAddr, consumerapp.Bech32PrefixAccPub) + cfg.SetBech32PrefixForValidator(consumerapp.Bech32PrefixValAddr, consumerapp.Bech32PrefixValPub) + cfg.SetBech32PrefixForConsensusNode(consumerapp.Bech32PrefixConsAddr, consumerapp.Bech32PrefixConsPub) cfg.SetAddressVerifier(wasmtypes.VerifyAddressLen()) cfg.Seal() @@ -62,7 +63,7 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { WithLegacyAmino(encodingConfig.Amino). WithInput(os.Stdin). WithAccountRetriever(authtypes.AccountRetriever{}). - WithHomeDir(app.DefaultNodeHome). + WithHomeDir(consumerapp.DefaultNodeHome). WithViper("") // In wasmd, we don't use any prefix for env variables. rootCmd := &cobra.Command{ @@ -152,23 +153,23 @@ func initAppConfig() (string, interface{}) { } func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { - gentxModule, ok := app.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic) + gentxModule, ok := consumerapp.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic) if !ok { panic(fmt.Errorf("expected %s module to be an instance of type %T", genutiltypes.ModuleName, genutil.AppModuleBasic{})) } rootCmd.AddCommand( - genutilcli.InitCmd(app.ModuleBasics, app.DefaultNodeHome), - genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome, gentxModule.GenTxValidator), + genutilcli.InitCmd(consumerapp.ModuleBasics, consumerapp.DefaultNodeHome), + genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, consumerapp.DefaultNodeHome, gentxModule.GenTxValidator), // testnetCmd(app.ModuleBasics, banktypes.GenesisBalancesIterator{}), - AddGenesisAccountCmd(app.DefaultNodeHome), + cmd.AddGenesisAccountCmd(consumerapp.DefaultNodeHome), debug.Cmd(), config.Cmd(), - genutilcli.GenTxCmd(app.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome), - genutilcli.ValidateGenesisCmd(app.ModuleBasics), + genutilcli.GenTxCmd(consumerapp.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, consumerapp.DefaultNodeHome), + genutilcli.ValidateGenesisCmd(consumerapp.ModuleBasics), pruning.PruningCmd(newApp), ) - server.AddCommands(rootCmd, app.DefaultNodeHome, newApp, appExport, addModuleInitFlags) + server.AddCommands(rootCmd, consumerapp.DefaultNodeHome, newApp, appExport, addModuleInitFlags) // add keybase, auxiliary RPC, query, and tx child commands rootCmd.AddCommand( @@ -176,7 +177,7 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { genesisCommand(encodingConfig), queryCommand(), txCommand(), - keys.Commands(app.DefaultNodeHome), + keys.Commands(consumerapp.DefaultNodeHome), ) // add rosetta rootCmd.AddCommand(rosettaCmd.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) @@ -189,7 +190,7 @@ func addModuleInitFlags(startCmd *cobra.Command) { // 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, app.ModuleBasics, app.DefaultNodeHome) + cmd := genutilcli.GenesisCoreCommand(encodingConfig.TxConfig, consumerapp.ModuleBasics, consumerapp.DefaultNodeHome) for _, subCmd := range cmds { cmd.AddCommand(subCmd) @@ -215,7 +216,7 @@ func queryCommand() *cobra.Command { authcmd.QueryTxCmd(), ) - app.ModuleBasics.AddQueryCommands(cmd) + consumerapp.ModuleBasics.AddQueryCommands(cmd) return cmd } @@ -241,7 +242,7 @@ func txCommand() *cobra.Command { authcmd.GetAuxToFeeCommand(), ) - app.ModuleBasics.AddTxCommands(cmd) + consumerapp.ModuleBasics.AddTxCommands(cmd) return cmd } @@ -260,7 +261,7 @@ func newApp( wasmOpts = append(wasmOpts, wasmkeeper.WithVMCacheMetrics(prometheus.DefaultRegisterer)) } - return app.NewMeshApp(logger, db, traceStore, true, appOpts, wasmOpts, baseappOptions...) + return consumerapp.NewMeshConsumerApp(logger, db, traceStore, true, appOpts, wasmOpts, baseappOptions...) } // appExport creates a new wasm app (optionally at a given height) and exports state. @@ -274,7 +275,7 @@ func appExport( appOpts servertypes.AppOptions, modulesToExport []string, ) (servertypes.ExportedApp, error) { - var wasmApp *app.MeshApp + var wasmApp *consumerapp.MeshConsumerApp homePath, ok := appOpts.Get(flags.FlagHome).(string) if !ok || homePath == "" { return servertypes.ExportedApp{}, errors.New("application home is not set") @@ -290,7 +291,7 @@ func appExport( appOpts = viperAppOpts var emptyWasmOpts []wasmkeeper.Option - wasmApp = app.NewMeshApp( + wasmApp = consumerapp.NewMeshConsumerApp( logger, db, traceStore, diff --git a/demo/cmd/meshproviderd/main.go b/demo/cmd/meshproviderd/main.go new file mode 100644 index 00000000..dc5ea9db --- /dev/null +++ b/demo/cmd/meshproviderd/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/server" + svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + + providerapp "github.com/osmosis-labs/mesh-security-sdk/demo/app/provider" +) + +func main() { + rootCmd, _ := NewRootCmd() + + if err := svrcmd.Execute(rootCmd, "", providerapp.DefaultNodeHome); err != nil { + switch e := err.(type) { + case server.ErrorCode: + os.Exit(e.Code) + + default: + os.Exit(1) + } + } +} diff --git a/demo/cmd/meshproviderd/root.go b/demo/cmd/meshproviderd/root.go new file mode 100644 index 00000000..94351915 --- /dev/null +++ b/demo/cmd/meshproviderd/root.go @@ -0,0 +1,310 @@ +package main + +import ( + "errors" + "fmt" + "io" + "os" + + "github.com/CosmWasm/wasmd/x/wasm" + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + dbm "github.com/cometbft/cometbft-db" + tmcfg "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/libs/log" + "github.com/prometheus/client_golang/prometheus" + "github.com/spf13/cast" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + rosettaCmd "cosmossdk.io/tools/rosetta/cmd" + + "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/server" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" + servertypes "github.com/cosmos/cosmos-sdk/server/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/crisis" + genutil "github.com/cosmos/cosmos-sdk/x/genutil" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + + "github.com/osmosis-labs/mesh-security-sdk/demo/app/params" + providerapp "github.com/osmosis-labs/mesh-security-sdk/demo/app/provider" + "github.com/osmosis-labs/mesh-security-sdk/demo/cmd" +) + +// NewRootCmd creates a new root command for wasmd. It is called once in the +// main function. +func NewRootCmd() (*cobra.Command, params.EncodingConfig) { + encodingConfig := providerapp.MakeEncodingConfig() + + cfg := sdk.GetConfig() + cfg.SetBech32PrefixForAccount(providerapp.Bech32PrefixAccAddr, providerapp.Bech32PrefixAccPub) + cfg.SetBech32PrefixForValidator(providerapp.Bech32PrefixValAddr, providerapp.Bech32PrefixValPub) + cfg.SetBech32PrefixForConsensusNode(providerapp.Bech32PrefixConsAddr, providerapp.Bech32PrefixConsPub) + cfg.SetAddressVerifier(wasmtypes.VerifyAddressLen()) + cfg.Seal() + + initClientCtx := client.Context{}. + WithCodec(encodingConfig.Marshaler). + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithInput(os.Stdin). + WithAccountRetriever(authtypes.AccountRetriever{}). + WithHomeDir(providerapp.DefaultNodeHome). + WithViper("") // In wasmd, we don't use any prefix for env variables. + + rootCmd := &cobra.Command{ + Use: version.AppName, + Short: "Wasm Daemon (server)", + 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, encodingConfig +} + +// initTendermintConfig helps to override default Tendermint Config values. +// return tmcfg.DefaultConfig if no custom configuration is required for the application. +func initTendermintConfig() *tmcfg.Config { + cfg := tmcfg.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. + + type CustomAppConfig struct { + serverconfig.Config + + Wasm wasmtypes.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 + // providerapp.toml. If left empty by validators, the node will halt on startup. + // However, the chain developer can set a default providerapp.toml value for their + // validators here. + // + // In summary: + // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their + // own providerapp.toml config, + // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their + // own providerapp.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: wasmtypes.DefaultWasmConfig(), + } + + customAppTemplate := serverconfig.DefaultConfigTemplate + + wasmtypes.DefaultConfigTemplate() + + return customAppTemplate, customAppConfig +} + +func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { + gentxModule, ok := providerapp.ModuleBasics[genutiltypes.ModuleName].(genutil.AppModuleBasic) + if !ok { + panic(fmt.Errorf("expected %s module to be an instance of type %T", genutiltypes.ModuleName, genutil.AppModuleBasic{})) + } + rootCmd.AddCommand( + genutilcli.InitCmd(providerapp.ModuleBasics, providerapp.DefaultNodeHome), + genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, providerapp.DefaultNodeHome, gentxModule.GenTxValidator), + // testnetCmd(providerapp.ModuleBasics, banktypes.GenesisBalancesIterator{}), + cmd.AddGenesisAccountCmd(providerapp.DefaultNodeHome), + debug.Cmd(), + config.Cmd(), + genutilcli.GenTxCmd(providerapp.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, providerapp.DefaultNodeHome), + genutilcli.ValidateGenesisCmd(providerapp.ModuleBasics), + pruning.PruningCmd(newApp), + ) + + server.AddCommands(rootCmd, providerapp.DefaultNodeHome, newApp, appExport, addModuleInitFlags) + + // add keybase, auxiliary RPC, query, and tx child commands + rootCmd.AddCommand( + rpc.StatusCommand(), + genesisCommand(encodingConfig), + queryCommand(), + txCommand(), + keys.Commands(providerapp.DefaultNodeHome), + ) + // add rosetta + rootCmd.AddCommand(rosettaCmd.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) +} + +func addModuleInitFlags(startCmd *cobra.Command) { + crisis.AddModuleInitFlags(startCmd) + wasm.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, providerapp.ModuleBasics, providerapp.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(), + ) + + providerapp.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(), + ) + + providerapp.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) + + var wasmOpts []wasmkeeper.Option + if cast.ToBool(appOpts.Get("telemetry.enabled")) { + wasmOpts = append(wasmOpts, wasmkeeper.WithVMCacheMetrics(prometheus.DefaultRegisterer)) + } + + return providerapp.NewMeshProviderApp(logger, db, traceStore, true, appOpts, wasmOpts, baseappOptions...) +} + +// appExport creates a new wasm app (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 wasmApp *providerapp.MeshProviderApp + homePath, ok := appOpts.Get(flags.FlagHome).(string) + if !ok || homePath == "" { + return servertypes.ExportedApp{}, errors.New("application home is 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 + + var emptyWasmOpts []wasmkeeper.Option + wasmApp = providerapp.NewMeshProviderApp( + logger, + db, + traceStore, + height == -1, + appOpts, + emptyWasmOpts, + ) + + if height != -1 { + if err := wasmApp.LoadHeight(height); err != nil { + return servertypes.ExportedApp{}, err + } + } + + return wasmApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport) +} diff --git a/demo/go.mod b/demo/go.mod index 665182e6..98b0cdbe 100644 --- a/demo/go.mod +++ b/demo/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/CosmWasm/wasmd v0.45.0 - github.com/CosmWasm/wasmvm v1.5.0 // indirect + github.com/CosmWasm/wasmvm v1.5.0 github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/cosmos-sdk v0.47.5 github.com/cosmos/gogogateway v1.2.0 // indirect diff --git a/demo/wasmbinding/message_plugin.go b/demo/wasmbinding/message_plugin.go new file mode 100644 index 00000000..3189e522 --- /dev/null +++ b/demo/wasmbinding/message_plugin.go @@ -0,0 +1,39 @@ +package contract + +import ( + "encoding/json" + + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + + errorsmod "cosmossdk.io/errors" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + consumermsg "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/contract" +) + +type ConsumerKeeper interface { + HandleBondMsg(ctx sdk.Context, actor sdk.AccAddress, bondMsg *consumermsg.BondMsg) ([]sdk.Event, [][]byte, error) + HandleUnbondMsg(ctx sdk.Context, actor sdk.AccAddress, unbondMsg *consumermsg.UnbondMsg) ([]sdk.Event, [][]byte, error) +} + +type CustomMessenger struct { + consKeeper ConsumerKeeper +} + +// DispatchMsg executes on the contractMsg. +func (h CustomMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, error) { + if msg.Custom != nil { + var contractMsg CustomMsg + if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil { + return nil, nil, errorsmod.Wrap(err, "mesh security msg") + } + + if contractMsg.Bond != nil { + return h.consKeeper.HandleBondMsg(ctx, contractAddr, contractMsg.Bond) + } + if contractMsg.Unbond != nil { + return h.consKeeper.HandleUnbondMsg(ctx, contractAddr, contractMsg.Unbond) + } + } + return nil, nil, wasmtypes.ErrUnknownMsg +} diff --git a/demo/wasmbinding/msg.go b/demo/wasmbinding/msg.go new file mode 100644 index 00000000..5b013b2b --- /dev/null +++ b/demo/wasmbinding/msg.go @@ -0,0 +1,12 @@ +package contract + +import ( + consumermsg "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/contract" +) + +type ( + CustomMsg struct { + Bond *consumermsg.BondMsg `json:"bond,omitempty"` + Unbond *consumermsg.UnbondMsg `json:"unbond,omitempty"` + } +) diff --git a/provider.Dockerfile b/provider.Dockerfile new file mode 100644 index 00000000..d4871400 --- /dev/null +++ b/provider.Dockerfile @@ -0,0 +1,41 @@ +FROM golang:1.20-alpine3.17 AS go-builder + +RUN apk add --no-cache ca-certificates build-base git + +WORKDIR /code + +# Download dependencies and CosmWasm libwasmvm if found. +ADD demo/go.mod demo/go.sum ./ + +#ADD https://github.com/CosmWasm/wasmvm/releases/download/v$wasmvm/libwasmvm_muslc.$arch.a /lib/libwasmvm_muslc.$arch.a +## Download +RUN set -eux; \ + WASM_VERSION=v$(go list -m github.com/CosmWasm/wasmvm | cut -d" " -f2 | cut -d"v" -f2); \ + echo $WASM_VERSION; \ + wget -O /lib/libwasmvm_muslc.a https://github.com/CosmWasm/wasmvm/releases/download/${WASM_VERSION}/libwasmvm_muslc.$(uname -m).a + +# Copy over code +COPY . /code + +# force it to use static lib (from above) not standard libgo_cosmwasm.so file +# then log output of file /code/bin/meshd +# then ensure static linking +RUN cd demo/ && LEDGER_ENABLED=false BUILD_TAGS=muslc LINK_STATICALLY=true make build \ + && file /code/demo/build/meshproviderd \ + && echo "Ensuring binary is statically linked ..." \ + && (file /code/demo/build/meshproviderd | grep "statically linked") + +# -------------------------------------------------------- +FROM alpine:3.17 + +COPY --from=go-builder /code/demo/build/meshproviderd /usr/bin/meshproviderd + +# Install dependencies used for Starship +RUN apk add --no-cache curl make bash jq sed + +WORKDIR /opt + +# rest server, tendermint p2p, tendermint rpc +EXPOSE 1317 26656 26657 + +CMD ["/usr/bin/meshproviderd", "version"] diff --git a/tests/e2e/e2e.go b/tests/e2e/e2e.go index 83f9dda5..98ad71ec 100644 --- a/tests/e2e/e2e.go +++ b/tests/e2e/e2e.go @@ -19,7 +19,8 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - "github.com/osmosis-labs/mesh-security-sdk/demo/app" + consumerapp "github.com/osmosis-labs/mesh-security-sdk/demo/app/consumer" + providerapp "github.com/osmosis-labs/mesh-security-sdk/demo/app/provider" ) var ( @@ -38,14 +39,18 @@ func buildPathToWasm(fileName string) string { func NewIBCCoordinator(t *testing.T, n int, opts ...[]wasmkeeper.Option) *ibctesting.Coordinator { return ibctesting.NewCoordinatorX(t, n, func(t *testing.T, valSet *types.ValidatorSet, genAccs []authtypes.GenesisAccount, chainID string, opts []wasmkeeper.Option, balances ...banktypes.Balance) ibctesting.ChainApp { - return app.SetupWithGenesisValSet(t, valSet, genAccs, chainID, opts, balances...) + if chainID == ibctesting.GetChainID(1) { + return consumerapp.SetupWithGenesisValSet(t, valSet, genAccs, chainID, opts, balances...) + } else { + return providerapp.SetupWithGenesisValSet(t, valSet, genAccs, chainID, opts, balances...) + } }, opts..., ) } -func submitGovProposal(t *testing.T, chain *TestChain, msgs ...sdk.Msg) uint64 { - chainApp := chain.App.(*app.MeshApp) +func submitProviderGovProposal(t *testing.T, chain *TestChain, msgs ...sdk.Msg) uint64 { + chainApp := chain.App.(*providerapp.MeshProviderApp) govParams := chainApp.GovKeeper.GetParams(chain.GetContext()) govMsg, err := govv1.NewMsgSubmitProposal(msgs, govParams.MinDeposit, chain.SenderAccount.GetAddress().String(), "", "my title", "my summary") require.NoError(t, err) @@ -56,12 +61,41 @@ func submitGovProposal(t *testing.T, chain *TestChain, msgs ...sdk.Msg) uint64 { return id } -func voteAndPassGovProposal(t *testing.T, chain *TestChain, proposalID uint64) { +func voteAndPassProviderGovProposal(t *testing.T, chain *TestChain, proposalID uint64) { vote := govv1.NewMsgVote(chain.SenderAccount.GetAddress(), proposalID, govv1.OptionYes, "testing") _, err := chain.SendMsgs(vote) require.NoError(t, err) - chainApp := chain.App.(*app.MeshApp) + chainApp := chain.App.(*providerapp.MeshProviderApp) + govParams := chainApp.GovKeeper.GetParams(chain.GetContext()) + + coord := chain.Coordinator + coord.IncrementTimeBy(*govParams.VotingPeriod) + coord.CommitBlock(chain.IBCTestChain()) + + rsp, err := chainApp.GovKeeper.Proposal(sdk.WrapSDKContext(chain.GetContext()), &govv1.QueryProposalRequest{ProposalId: proposalID}) + require.NoError(t, err) + require.Equal(t, rsp.Proposal.Status, govv1.ProposalStatus_PROPOSAL_STATUS_PASSED) +} + +func submitConsumerGovProposal(t *testing.T, chain *TestChain, msgs ...sdk.Msg) uint64 { + chainApp := chain.App.(*consumerapp.MeshConsumerApp) + govParams := chainApp.GovKeeper.GetParams(chain.GetContext()) + govMsg, err := govv1.NewMsgSubmitProposal(msgs, govParams.MinDeposit, chain.SenderAccount.GetAddress().String(), "", "my title", "my summary") + require.NoError(t, err) + rsp, err := chain.SendMsgs(govMsg) + require.NoError(t, err) + id := rsp.MsgResponses[0].GetCachedValue().(*govv1.MsgSubmitProposalResponse).ProposalId + require.NotEmpty(t, id) + return id +} + +func voteAndPassConsumerGovProposal(t *testing.T, chain *TestChain, proposalID uint64) { + vote := govv1.NewMsgVote(chain.SenderAccount.GetAddress(), proposalID, govv1.OptionYes, "testing") + _, err := chain.SendMsgs(vote) + require.NoError(t, err) + + chainApp := chain.App.(*consumerapp.MeshConsumerApp) govParams := chainApp.GovKeeper.GetParams(chain.GetContext()) coord := chain.Coordinator @@ -97,8 +131,8 @@ type example struct { Coordinator *ibctesting.Coordinator ConsumerChain *TestChain ProviderChain *TestChain - ConsumerApp *app.MeshApp - ProviderApp *app.MeshApp + ConsumerApp *consumerapp.MeshConsumerApp + ProviderApp *providerapp.MeshProviderApp IbcPath *ibctesting.Path ProviderDenom string ConsumerDenom string @@ -108,14 +142,14 @@ type example struct { func setupExampleChains(t *testing.T) example { coord := NewIBCCoordinator(t, 2) - provChain := coord.GetChain(ibctesting2.GetChainID(1)) - consChain := coord.GetChain(ibctesting2.GetChainID(2)) + consChain := coord.GetChain(ibctesting2.GetChainID(1)) + provChain := coord.GetChain(ibctesting2.GetChainID(2)) return example{ Coordinator: coord, ConsumerChain: NewTestChain(t, consChain), ProviderChain: NewTestChain(t, provChain), - ConsumerApp: consChain.App.(*app.MeshApp), - ProviderApp: provChain.App.(*app.MeshApp), + ConsumerApp: consChain.App.(*consumerapp.MeshConsumerApp), + ProviderApp: provChain.App.(*providerapp.MeshProviderApp), IbcPath: ibctesting.NewPath(consChain, provChain), ProviderDenom: sdk.DefaultBondDenom, ConsumerDenom: sdk.DefaultBondDenom, diff --git a/tests/e2e/slashing_test.go b/tests/e2e/slashing_test.go index 63c93de2..c68c2079 100644 --- a/tests/e2e/slashing_test.go +++ b/tests/e2e/slashing_test.go @@ -84,7 +84,7 @@ func TestSlashingScenario1(t *testing.T) { // Validator 1 on the Consumer chain is jailed myExtValidator1ConsAddr := sdk.ConsAddress(x.ConsumerChain.Vals.Validators[1].PubKey.Address()) - jailValidator(t, myExtValidator1ConsAddr, x.Coordinator, x.ConsumerChain, x.ConsumerApp) + jailConsumerValidator(t, myExtValidator1ConsAddr, x.Coordinator, x.ConsumerChain, x.ConsumerApp) x.ConsumerChain.NextBlock() @@ -171,7 +171,7 @@ func TestSlashingScenario2(t *testing.T) { // Validator 1 on the Consumer chain is jailed myExtValidator1ConsAddr := sdk.ConsAddress(x.ConsumerChain.Vals.Validators[1].PubKey.Address()) - jailValidator(t, myExtValidator1ConsAddr, x.Coordinator, x.ConsumerChain, x.ConsumerApp) + jailConsumerValidator(t, myExtValidator1ConsAddr, x.Coordinator, x.ConsumerChain, x.ConsumerApp) x.ConsumerChain.NextBlock() @@ -258,7 +258,7 @@ func TestSlashingScenario3(t *testing.T) { // Validator 1 on the Consumer chain is jailed myExtValidator1ConsAddr := sdk.ConsAddress(x.ConsumerChain.Vals.Validators[1].PubKey.Address()) - jailValidator(t, myExtValidator1ConsAddr, x.Coordinator, x.ConsumerChain, x.ConsumerApp) + jailConsumerValidator(t, myExtValidator1ConsAddr, x.Coordinator, x.ConsumerChain, x.ConsumerApp) x.ConsumerChain.NextBlock() @@ -285,6 +285,59 @@ func TestSlashingScenario3(t *testing.T) { require.Equal(t, 0, providerCli.QueryVaultFreeBalance()) // 185 - max(32, 185) = 185 - 185 = 0 } +func TestSlashingProviderChain(t *testing.T) { + x := setupExampleChains(t) + _, _, providerCli := setupMeshSecurity(t, x) + + // Provider chain + // ============== + // Deposit - A user deposits the vault denom to provide some collateral to their account + execMsg := fmt.Sprintf(`{"bond":{"amount":{"denom":"%s", "amount":"200000000"}}}`, x.ProviderDenom) + providerCli.MustExecVault(execMsg) + + // Stake Locally - A user triggers a local staking action to a chosen validator. + + myLocalValidator1Addr := sdk.ValAddress(x.ProviderChain.Vals.Validators[1].Address) + myLocalValidator1 := myLocalValidator1Addr.String() + execLocalStakingMsg := fmt.Sprintf(`{"stake_local":{"amount": {"denom":%q, "amount":"%d"}, "msg":%q}}`, + x.ProviderDenom, 100_000_000, + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"validator": "%s"}`, myLocalValidator1)))) + providerCli.MustExecVault(execLocalStakingMsg) + + myLocalValidator2 := sdk.ValAddress(x.ProviderChain.Vals.Validators[2].Address).String() + execLocalStakingMsg = fmt.Sprintf(`{"stake_local":{"amount": {"denom":%q, "amount":"%d"}, "msg":%q}}`, + x.ProviderDenom, 50_000_000, + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"validator": "%s"}`, myLocalValidator2)))) + providerCli.MustExecVault(execLocalStakingMsg) + + require.Equal(t, 200_000_000, providerCli.QueryVaultBalance()) + // Check max lien + require.Equal(t, 150_000_000, providerCli.QueryMaxLien()) + // Check slashable amount + require.Equal(t, 30_000_000, providerCli.QuerySlashableAmount()) + // Check free collateral + require.Equal(t, 50_000_000, providerCli.QueryVaultFreeBalance()) + + validator1, _ := x.ProviderApp.StakingKeeper.GetValidator(x.ProviderChain.GetContext(), myLocalValidator1Addr) + myLocalValidator1ConsAddr, err := validator1.GetConsAddr() + require.NoError(t, err) + jailProviderValidator(t, myLocalValidator1ConsAddr, x.Coordinator, x.ProviderChain, x.ProviderApp) + + x.ProviderChain.NextBlock() + + // Assert that the validator's stake has been slashed + // and that the validator has been jailed + validator1, _ = x.ProviderApp.StakingKeeper.GetValidator(x.ProviderChain.GetContext(), myLocalValidator1Addr) + require.True(t, validator1.IsJailed()) + require.Equal(t, validator1.GetTokens(), sdk.NewInt(90_900_000)) + + // Assert sudo msg has been sent to contract + require.Equal(t, 190_000_000, providerCli.QueryVaultBalance()) + require.Equal(t, 140_000_000, providerCli.QueryMaxLien()) + require.Equal(t, 28_000_000, providerCli.QuerySlashableAmount()) + require.Equal(t, 50_000_000, providerCli.QueryVaultFreeBalance()) +} + func TestValidatorTombstone(t *testing.T) { x := setupExampleChains(t) consumerCli, _, providerCli := setupMeshSecurity(t, x) @@ -355,7 +408,7 @@ func TestValidatorTombstone(t *testing.T) { // Validator 1 on the Consumer chain is tombstoned myExtValidator1ConsAddr := sdk.ConsAddress(x.ConsumerChain.Vals.Validators[1].PubKey.Address()) - tombstoneValidator(t, myExtValidator1ConsAddr, myExtValidator1, x.ConsumerChain, x.ConsumerApp) + tombstoneConsumerValidator(t, myExtValidator1ConsAddr, myExtValidator1, x.ConsumerChain, x.ConsumerApp) x.ConsumerChain.NextBlock() @@ -399,10 +452,11 @@ func TestSlasingImmediateUnbond(t *testing.T) { providerCli.MustExecVault(execMsg) // Stake Locally - A user triggers a local staking action to a chosen validator. - myLocalValidatorAddr := sdk.ValAddress(x.ProviderChain.Vals.Validators[0].Address).String() + myLocalValidatorAddr := sdk.ValAddress(x.ProviderChain.Vals.Validators[0].Address) + myLocalValidator := myLocalValidatorAddr.String() execLocalStakingMsg := fmt.Sprintf(`{"stake_local":{"amount": {"denom":%q, "amount":"%d"}, "msg":%q}}`, x.ProviderDenom, 100_000_000, - base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"validator": "%s"}`, myLocalValidatorAddr)))) + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"validator": "%s"}`, myLocalValidator)))) providerCli.MustExecVault(execLocalStakingMsg) // Check slashable amount @@ -411,26 +465,29 @@ func TestSlasingImmediateUnbond(t *testing.T) { require.Equal(t, 100_000_000, providerCli.QueryVaultFreeBalance()) // Validator on the provider chain is jailed - myLocalValidatorConsAddr := sdk.ConsAddress(x.ProviderChain.Vals.Validators[0].PubKey.Address()) - jailValidator(t, myLocalValidatorConsAddr, x.Coordinator, x.ProviderChain, x.ProviderApp) + validator, _ := x.ProviderApp.StakingKeeper.GetValidator(x.ProviderChain.GetContext(), myLocalValidatorAddr) + myLocalValidatorConsAddr, err := validator.GetConsAddr() + require.NoError(t, err) + jailProviderValidator(t, myLocalValidatorConsAddr, x.Coordinator, x.ProviderChain, x.ProviderApp) x.ProviderChain.NextBlock() - // Check new collateral - require.Equal(t, 200_000_000, providerCli.QueryVaultBalance()) - // Check new max lien - require.Equal(t, 100_000_000, providerCli.QueryMaxLien()) - // Check new slashable amount - require.Equal(t, 20_000_000, providerCli.QuerySlashableAmount()) - // Check new free collateral + validator, found := x.ProviderApp.StakingKeeper.GetValidator(x.ProviderChain.GetContext(), myLocalValidatorAddr) + require.True(t, found) + require.True(t, validator.IsJailed()) + + // Assert sudo msg has been sent to contract + require.Equal(t, 190_000_000, providerCli.QueryVaultBalance()) + require.Equal(t, 90000000, providerCli.QueryMaxLien()) + require.Equal(t, 18_000_000, providerCli.QuerySlashableAmount()) require.Equal(t, 100_000_000, providerCli.QueryVaultFreeBalance()) // Get native staking proxy contract nativeStakingProxy := providerCli.QueryNativeStakingProxyByOwner(x.ProviderChain.SenderAccount.GetAddress().String()) execMsg = fmt.Sprintf(`{"unstake": {"validator":%q,"amount": {"denom":%q, "amount":"%d"}}}`, - myLocalValidatorAddr, x.ProviderDenom, 10_000_000) - _, err := providerCli.Exec(nativeStakingProxy, execMsg) + myLocalValidator, x.ProviderDenom, 10_000_000) + _, err = providerCli.Exec(nativeStakingProxy, execMsg) require.NoError(t, err) x.ProviderChain.NextBlock() @@ -439,12 +496,12 @@ func TestSlasingImmediateUnbond(t *testing.T) { require.NoError(t, err) // Check new collateral - require.Equal(t, 200_000_000, providerCli.QueryVaultBalance()) + require.Equal(t, 190_000_000, providerCli.QueryVaultBalance()) // Check new max lien // Max lien decrease as release_unbonded - require.Equal(t, 90_000_001, providerCli.QueryMaxLien()) + require.Equal(t, 80_000_001, providerCli.QueryMaxLien()) // Check new slashable amount - require.Equal(t, 18000001, providerCli.QuerySlashableAmount()) - // Check new free collateral - require.Equal(t, 109999999, providerCli.QueryVaultFreeBalance()) + require.Equal(t, 16_000_001, providerCli.QuerySlashableAmount()) + // Check new free collateral = 100_000_000 + 9_999_999(10_000_000 convert to consumer tokens and convert back) + require.Equal(t, 109_999_999, providerCli.QueryVaultFreeBalance()) } diff --git a/tests/e2e/test_client.go b/tests/e2e/test_client.go index 7df02a23..51fdecce 100644 --- a/tests/e2e/test_client.go +++ b/tests/e2e/test_client.go @@ -23,7 +23,8 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - "github.com/osmosis-labs/mesh-security-sdk/demo/app" + consumerapp "github.com/osmosis-labs/mesh-security-sdk/demo/app/consumer" + providerapp "github.com/osmosis-labs/mesh-security-sdk/demo/app/provider" "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity" "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/keeper" "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/types" @@ -100,7 +101,7 @@ func NewProviderClient(t *testing.T, chain *TestChain) *TestProviderClient { func (tc *TestChain) SendMsgsWithSigner(privKey cryptotypes.PrivKey, signer *authtypes.BaseAccount, msgs ...sdk.Msg) (*sdk.Result, error) { // ensure the chain has the latest time tc.Coordinator.UpdateTimeForChain(tc.TestChain) - _, r, gotErr := app.SignAndDeliverWithoutCommit( + _, r, gotErr := providerapp.SignAndDeliverWithoutCommit( tc.t, tc.TxConfig, tc.App.GetBaseApp(), @@ -134,7 +135,7 @@ type ProviderContracts struct { ExternalStaking sdk.AccAddress } -func (p *TestProviderClient) BootstrapContracts(provApp *app.MeshApp, connId, portID string) ProviderContracts { +func (p *TestProviderClient) BootstrapContracts(provApp *providerapp.MeshProviderApp, connId, portID string) ProviderContracts { var ( unbondingPeriod = 21 * 24 * 60 * 60 // 21 days - make configurable? localSlashRatioDoubleSign = "0.20" @@ -256,19 +257,6 @@ func (p TestProviderClient) ExecWithSigner(privKey cryptotypes.PrivKey, signer * return rsp, err } -// MustExecGovProposal submit and vote yes on proposal -func (p TestProviderClient) MustExecParamsChangeProposal(provApp *app.MeshApp, vault, nativeStaking string) { - msg := &providertypes.MsgUpdateParams{ - Authority: provApp.MeshSecKeeper.GetAuthority(), - Params: providertypes.Params{ - VaultAddress: vault, - NativeStakingAddress: nativeStaking, - }, - } - proposalID := submitGovProposal(p.t, p.chain, msg) - voteAndPassGovProposal(p.t, p.chain, proposalID) -} - func (p TestProviderClient) MustFailExecVault(payload string, funds ...sdk.Coin) error { rsp, err := p.Exec(p.Contracts.Vault, payload, funds...) require.Error(p.t, err, "Response: %v", rsp) @@ -288,15 +276,17 @@ func (p TestProviderClient) ExecStakeRemote(val string, amt sdk.Coin) error { return err } -func (p TestProviderClient) QueryExtStakingAmount(user, validator string) int { - qRsp := p.QueryExtStaking(Query{ - "stake": { - "user": user, - "validator": validator, +// MustExecGovProposal submit and vote yes on proposal +func (p TestProviderClient) MustExecParamsChangeProposal(provApp *providerapp.MeshProviderApp, vault, nativeStaking string) { + msg := &providertypes.MsgUpdateParams{ + Authority: provApp.MeshSecKeeper.GetAuthority(), + Params: providertypes.Params{ + VaultAddress: vault, + NativeStakingAddress: nativeStaking, }, - }) - require.Contains(p.t, qRsp, "stake") - return ParseHighLow(p.t, qRsp["stake"]).Low + } + proposalID := submitProviderGovProposal(p.t, p.chain, msg) + voteAndPassProviderGovProposal(p.t, p.chain, proposalID) } func (p TestProviderClient) QueryNativeStakingProxyByOwner(user string) sdk.AccAddress { @@ -311,6 +301,22 @@ func (p TestProviderClient) QueryNativeStakingProxyByOwner(user string) sdk.AccA return a } + +func (p TestProviderClient) QueryNativeStaking(q Query) QueryResponse { + return Querier(p.t, p.chain)(p.Contracts.NativeStaking.String(), q) +} + +func (p TestProviderClient) QueryExtStakingAmount(user, validator string) int { + qRsp := p.QueryExtStaking(Query{ + "stake": { + "user": user, + "validator": validator, + }, + }) + require.Contains(p.t, qRsp, "stake") + return ParseHighLow(p.t, qRsp["stake"]).Low +} + func (p TestProviderClient) QueryExtStaking(q Query) QueryResponse { return Querier(p.t, p.chain)(p.Contracts.ExternalStaking.String(), q) } @@ -319,10 +325,6 @@ func (p TestProviderClient) QueryVault(q Query) QueryResponse { return Querier(p.t, p.chain)(p.Contracts.Vault.String(), q) } -func (p TestProviderClient) QueryNativeStaking(q Query) QueryResponse { - return Querier(p.t, p.chain)(p.Contracts.NativeStaking.String(), q) -} - type HighLowType struct { High, Low int } @@ -388,11 +390,11 @@ type TestConsumerClient struct { t *testing.T chain *TestChain contracts ConsumerContract - app *app.MeshApp + app *consumerapp.MeshConsumerApp } func NewConsumerClient(t *testing.T, chain *TestChain) *TestConsumerClient { - return &TestConsumerClient{t: t, chain: chain, app: chain.App.(*app.MeshApp)} + return &TestConsumerClient{t: t, chain: chain, app: chain.App.(*consumerapp.MeshConsumerApp)} } type ConsumerContract struct { @@ -468,13 +470,13 @@ func (p *TestConsumerClient) MustEnableVirtualStaking(maxCap sdk.Coin) { Contract: p.contracts.staking.String(), MaxCap: maxCap, } - p.MustExecGovProposal(govProposal) + p.MustExecConsumerGovProposal(govProposal) } // MustExecGovProposal submit and vote yes on proposal -func (p *TestConsumerClient) MustExecGovProposal(msg *types.MsgSetVirtualStakingMaxCap) { - proposalID := submitGovProposal(p.t, p.chain, msg) - voteAndPassGovProposal(p.t, p.chain, proposalID) +func (p *TestConsumerClient) MustExecConsumerGovProposal(msg *types.MsgSetVirtualStakingMaxCap) { + proposalID := submitConsumerGovProposal(p.t, p.chain, msg) + voteAndPassConsumerGovProposal(p.t, p.chain, proposalID) } func (p *TestConsumerClient) QueryMaxCap() types.QueryVirtualStakingMaxCapLimitResponse { diff --git a/tests/e2e/valset_test.go b/tests/e2e/valset_test.go index 223f00a7..84792946 100644 --- a/tests/e2e/valset_test.go +++ b/tests/e2e/valset_test.go @@ -19,7 +19,8 @@ import ( slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/osmosis-labs/mesh-security-sdk/demo/app" + consumerapp "github.com/osmosis-labs/mesh-security-sdk/demo/app/consumer" + providerapp "github.com/osmosis-labs/mesh-security-sdk/demo/app/provider" ) func TestValsetTransitions(t *testing.T) { @@ -71,7 +72,7 @@ func TestValsetTransitions(t *testing.T) { "active to jailed": { setup: setupVal, doTransition: func(t *testing.T, val mock.PV, x example) { - jailValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), x.Coordinator, x.ConsumerChain, x.ConsumerApp) + jailConsumerValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), x.Coordinator, x.ConsumerChain, x.ConsumerApp) }, assertPackets: func(t *testing.T, packets []channeltypes.Packet) { require.Len(t, packets, 1) @@ -83,13 +84,13 @@ func TestValsetTransitions(t *testing.T) { "jailed to active": { setup: func(t *testing.T, x example) mock.PV { val := CreateNewValidator(t, operatorKeys, x.ConsumerChain, 200) - jailValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), x.Coordinator, x.ConsumerChain, x.ConsumerApp) + jailConsumerValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), x.Coordinator, x.ConsumerChain, x.ConsumerApp) x.ConsumerChain.NextBlock() require.NoError(t, x.Coordinator.RelayAndAckPendingPackets(x.IbcPath)) return val }, doTransition: func(t *testing.T, val mock.PV, x example) { - unjailValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), operatorKeys, x.Coordinator, x.ConsumerChain, x.ConsumerApp) + unjailConsumerValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), operatorKeys, x.Coordinator, x.ConsumerChain, x.ConsumerApp) }, assertPackets: func(t *testing.T, packets []channeltypes.Packet) { for _, v := range packets { @@ -105,7 +106,7 @@ func TestValsetTransitions(t *testing.T) { setup: func(t *testing.T, x example) mock.PV { val := CreateNewValidator(t, operatorKeys, x.ConsumerChain, 200) t.Log("jail validator") - jailValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), x.Coordinator, x.ConsumerChain, x.ConsumerApp) + jailConsumerValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), x.Coordinator, x.ConsumerChain, x.ConsumerApp) t.Log("Add new validator") otherOperator := secp256k1.GenPrivKey() @@ -121,7 +122,7 @@ func TestValsetTransitions(t *testing.T) { }, doTransition: func(t *testing.T, val mock.PV, x example) { t.Log("unjail") - unjailValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), operatorKeys, x.Coordinator, x.ConsumerChain, x.ConsumerApp) + unjailConsumerValidator(t, sdk.ConsAddress(val.PrivKey.PubKey().Address()), operatorKeys, x.Coordinator, x.ConsumerChain, x.ConsumerApp) }, assertPackets: func(t *testing.T, packets []channeltypes.Packet) { require.Len(t, packets, 1) @@ -165,7 +166,7 @@ func undelegate(t *testing.T, operatorKeys *secp256k1.PrivKey, amount sdkmath.In x.ConsumerChain.NextBlock() } -func jailValidator(t *testing.T, consAddr sdk.ConsAddress, coordinator *wasmibctesting.Coordinator, chain *TestChain, app *app.MeshApp) { +func jailConsumerValidator(t *testing.T, consAddr sdk.ConsAddress, coordinator *wasmibctesting.Coordinator, chain *TestChain, app *consumerapp.MeshConsumerApp) { ctx := chain.GetContext() signInfo, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, consAddr) require.True(t, found) @@ -180,7 +181,22 @@ func jailValidator(t *testing.T, consAddr sdk.ConsAddress, coordinator *wasmibct chain.NextBlock() } -func unjailValidator(t *testing.T, consAddr sdk.ConsAddress, operatorKeys *secp256k1.PrivKey, coordinator *wasmibctesting.Coordinator, chain *TestChain, app *app.MeshApp) { +func jailProviderValidator(t *testing.T, consAddr sdk.ConsAddress, coordinator *wasmibctesting.Coordinator, chain *TestChain, app *providerapp.MeshProviderApp) { + ctx := chain.GetContext() + signInfo, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, consAddr) + require.True(t, found) + // bump height to be > block window + coordinator.CommitNBlocks(chain.IBCTestChain(), 100) + ctx = chain.GetContext() + signInfo.MissedBlocksCounter = app.SlashingKeeper.MinSignedPerWindow(ctx) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, consAddr, signInfo) + power := app.StakingKeeper.GetLastValidatorPower(ctx, sdk.ValAddress(consAddr)) + app.SlashingKeeper.HandleValidatorSignature(ctx, cryptotypes.Address(consAddr), power, false) + // when updates trigger + chain.NextBlock() +} + +func unjailConsumerValidator(t *testing.T, consAddr sdk.ConsAddress, operatorKeys *secp256k1.PrivKey, coordinator *wasmibctesting.Coordinator, chain *TestChain, app *consumerapp.MeshConsumerApp) { // move clock aa, ok := app.SlashingKeeper.GetValidatorSigningInfo(chain.GetContext(), consAddr) require.True(t, ok) @@ -193,7 +209,7 @@ func unjailValidator(t *testing.T, consAddr sdk.ConsAddress, operatorKeys *secp2 chain.NextBlock() } -func tombstoneValidator(t *testing.T, consAddr sdk.ConsAddress, valAddr sdk.ValAddress, chain *TestChain, app *app.MeshApp) { +func tombstoneConsumerValidator(t *testing.T, consAddr sdk.ConsAddress, valAddr sdk.ValAddress, chain *TestChain, consApp *consumerapp.MeshConsumerApp) { e := &types.Equivocation{ Height: chain.GetContext().BlockHeight(), Power: 100, @@ -201,7 +217,7 @@ func tombstoneValidator(t *testing.T, consAddr sdk.ConsAddress, valAddr sdk.ValA ConsensusAddress: consAddr.String(), } // when - app.EvidenceKeeper.HandleEquivocationEvidence(chain.GetContext(), e) + consApp.EvidenceKeeper.HandleEquivocationEvidence(chain.GetContext(), e) chain.NextBlock() packets := chain.PendingSendPackets diff --git a/tests/testdata/mesh_converter.wasm.gz b/tests/testdata/mesh_converter.wasm.gz index 16534030..de7a8615 100644 Binary files a/tests/testdata/mesh_converter.wasm.gz and b/tests/testdata/mesh_converter.wasm.gz differ diff --git a/tests/testdata/mesh_external_staking.wasm.gz b/tests/testdata/mesh_external_staking.wasm.gz index 907cf82b..183db600 100644 Binary files a/tests/testdata/mesh_external_staking.wasm.gz and b/tests/testdata/mesh_external_staking.wasm.gz differ diff --git a/tests/testdata/mesh_native_staking.wasm.gz b/tests/testdata/mesh_native_staking.wasm.gz index 212174ec..26b0180c 100644 Binary files a/tests/testdata/mesh_native_staking.wasm.gz and b/tests/testdata/mesh_native_staking.wasm.gz differ diff --git a/tests/testdata/mesh_native_staking_proxy.wasm.gz b/tests/testdata/mesh_native_staking_proxy.wasm.gz index d4d9a4f2..28c5c55d 100644 Binary files a/tests/testdata/mesh_native_staking_proxy.wasm.gz and b/tests/testdata/mesh_native_staking_proxy.wasm.gz differ diff --git a/tests/testdata/mesh_osmosis_price_provider.wasm.gz b/tests/testdata/mesh_osmosis_price_provider.wasm.gz index 4d976637..88871b55 100644 Binary files a/tests/testdata/mesh_osmosis_price_provider.wasm.gz and b/tests/testdata/mesh_osmosis_price_provider.wasm.gz differ diff --git a/tests/testdata/mesh_remote_price_feed.wasm.gz b/tests/testdata/mesh_remote_price_feed.wasm.gz index 8ed3f646..163bff64 100644 Binary files a/tests/testdata/mesh_remote_price_feed.wasm.gz and b/tests/testdata/mesh_remote_price_feed.wasm.gz differ diff --git a/tests/testdata/mesh_simple_price_feed.wasm.gz b/tests/testdata/mesh_simple_price_feed.wasm.gz index b4570a0a..f3a2a984 100644 Binary files a/tests/testdata/mesh_simple_price_feed.wasm.gz and b/tests/testdata/mesh_simple_price_feed.wasm.gz differ diff --git a/tests/testdata/mesh_vault.wasm.gz b/tests/testdata/mesh_vault.wasm.gz index 730c65e6..f70dedae 100644 Binary files a/tests/testdata/mesh_vault.wasm.gz and b/tests/testdata/mesh_vault.wasm.gz differ diff --git a/tests/testdata/mesh_virtual_staking.wasm.gz b/tests/testdata/mesh_virtual_staking.wasm.gz index f3b293d8..2a227473 100644 Binary files a/tests/testdata/mesh_virtual_staking.wasm.gz and b/tests/testdata/mesh_virtual_staking.wasm.gz differ diff --git a/tests/testdata/version.txt b/tests/testdata/version.txt index dad38732..b623541b 100644 --- a/tests/testdata/version.txt +++ b/tests/testdata/version.txt @@ -1 +1 @@ -da87f16dba80c7e649a99d80f8c38f223a574e01 +7cf179e7c5f4a59264b062d7cefe24006f2ba84e diff --git a/version.txt b/version.txt new file mode 100644 index 00000000..3586f35a --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +c6a34ff3e3bad62c8ee76cc09647367f430973bb diff --git a/x/meshsecurityprovider/client/cli/query.go b/x/meshsecurityprovider/client/cli/query.go index 6d6a507c..0a96ea20 100644 --- a/x/meshsecurityprovider/client/cli/query.go +++ b/x/meshsecurityprovider/client/cli/query.go @@ -29,7 +29,6 @@ func GetQueryCmd() *cobra.Command { return meshsecurityproviderQueryCmd } - // GetCmdQueryParams implements a command to return the current parameters. func GetCmdQueryParams() *cobra.Command { cmd := &cobra.Command{ diff --git a/x/meshsecurityprovider/contract/out_message.go b/x/meshsecurityprovider/contract/out_message.go new file mode 100644 index 00000000..1dbc2a9b --- /dev/null +++ b/x/meshsecurityprovider/contract/out_message.go @@ -0,0 +1,14 @@ +package contract + +type ( + SudoMsg struct { + Jailing *ValidatorSlash `json:"jailing,omitempty"` + } + // ValidatorAddr alias for the Bech32 address string of sdk.ValAddress + ValidatorAddr = string + + ValidatorSlash struct { + Jailed []ValidatorAddr `json:"jailed"` + Tombstoned []ValidatorAddr `json:"tombstoned"` + } +) diff --git a/x/meshsecurityprovider/keeper/adapter.go b/x/meshsecurityprovider/keeper/adapter.go new file mode 100644 index 00000000..3fede0fb --- /dev/null +++ b/x/meshsecurityprovider/keeper/adapter.go @@ -0,0 +1,153 @@ +package keeper + +import ( + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + meshsecuritykeeper "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/keeper" + "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurity/types" +) + +var _ types.XStakingKeeper = &StakingKeeperAdapter{} + +// StakingKeeperAdapter is an adapter to enhance the vanilla sdk staking keeper with additional functionality +// required for MS. The methods match Osmosis SDK fork. +type StakingKeeperAdapter struct { + types.SDKStakingKeeper + bank types.SDKBankKeeper +} + +// NewStakingKeeperAdapter constructor +func NewStakingKeeperAdapter(k types.SDKStakingKeeper, b types.SDKBankKeeper) *StakingKeeperAdapter { + return &StakingKeeperAdapter{SDKStakingKeeper: k, bank: b} +} + +// InstantUndelegate allows another module account to undelegate while bypassing unbonding time. +// This function is a combination of Undelegate and CompleteUnbonding, +// but skips the creation and deletion of UnbondingDelegationEntry +// +// The code is copied from the Osmosis SDK fork https://github.com/osmosis-labs/cosmos-sdk/blob/v0.45.0x-osmo-v9.3/x/staking/keeper/delegation.go#L757 +func (s StakingKeeperAdapter) InstantUndelegate(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) (sdk.Coins, error) { + validator, found := s.GetValidator(ctx, valAddr) + if !found { + return nil, stakingtypes.ErrNoDelegatorForAddress + } + + returnAmount, err := s.Unbond(ctx, delAddr, valAddr, sharesAmount) + if err != nil { + return nil, err + } + + bondDenom := s.BondDenom(ctx) + + amt := sdk.NewCoin(bondDenom, returnAmount) + res := sdk.NewCoins(amt) + + moduleName := stakingtypes.NotBondedPoolName + if validator.IsBonded() { + moduleName = stakingtypes.BondedPoolName + } + err = s.bank.UndelegateCoinsFromModuleToAccount(ctx, moduleName, delAddr, res) + if err != nil { + return nil, err + } + return res, nil +} + +// StakingDecorator decorate vanilla staking keeper to capture the jail and unjail events +type StakingDecorator struct { + slashingtypes.StakingKeeper + k *Keeper +} + +// NewStakingDecorator constructor +func NewStakingDecorator(stakingKeeper slashingtypes.StakingKeeper, k *Keeper) *StakingDecorator { + return &StakingDecorator{StakingKeeper: stakingKeeper, k: k} +} + +// Slash captures the slash event and calls the decorated staking keeper slash method +func (s StakingDecorator) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, power int64, height int64, slashRatio sdk.Dec) math.Int { + if s.k.meshConsumer == nil { + return s.StakingKeeper.Slash(ctx, consAddr, power, height, slashRatio) + } + + val := s.StakingKeeper.ValidatorByConsAddr(ctx, consAddr) + totalSlashAmount := s.StakingKeeper.Slash(ctx, consAddr, power, height, slashRatio) + if val == nil { + meshsecuritykeeper.ModuleLogger(ctx). + Error("can not propagate slash: validator not found", "validator", consAddr.String()) + } else if err := s.k.meshConsumer.ScheduleSlashed(ctx, val.GetOperator(), power, height, totalSlashAmount, slashRatio); err != nil { + meshsecuritykeeper.ModuleLogger(ctx). + Error("can not propagate slash: schedule event", + "cause", err, + "validator", consAddr.String()) + } + return totalSlashAmount +} + +// SlashWithInfractionReason implementation doesn't require the infraction (types.Infraction) to work but is required by Interchain Security. +func (s StakingDecorator) SlashWithInfractionReason(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeight int64, power int64, slashFactor sdk.Dec, infraction stakingtypes.Infraction) math.Int { + // foward it to native-staking contract + params := s.k.GetParams(ctx) + nativeStakingAddr := sdk.MustAccAddressFromBech32(params.NativeStakingAddress) + + validator := s.StakingKeeper.ValidatorByConsAddr(ctx, consAddr) + + if infraction == stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN { + err := s.k.SendJailHandlingMsg(ctx, nativeStakingAddr, []string{}, []string{validator.GetOperator().String()}) + if err != nil { + meshsecuritykeeper.ModuleLogger(ctx). + Error("can not send tombstone handling message to native-staking contract") + } + } + + if infraction == stakingtypes.Infraction_INFRACTION_DOWNTIME { + err := s.k.SendJailHandlingMsg(ctx, nativeStakingAddr, []string{validator.GetOperator().String()}, []string{}) + if err != nil { + meshsecuritykeeper.ModuleLogger(ctx). + Error("can not send jail handling message to native-staking contract") + } + } + return s.Slash(ctx, consAddr, infractionHeight, power, slashFactor) +} + +// Jail captures the jail event and calls the decorated staking keeper jail method +func (s StakingDecorator) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { + if s.k.meshConsumer == nil { + s.StakingKeeper.Jail(ctx, consAddr) + } + + val := s.StakingKeeper.ValidatorByConsAddr(ctx, consAddr) + if val == nil { + meshsecuritykeeper.ModuleLogger(ctx). + Error("can not propagate jail: validator not found", "validator", consAddr.String()) + } else if err := s.k.meshConsumer.ScheduleJailed(ctx, val.GetOperator()); err != nil { + meshsecuritykeeper.ModuleLogger(ctx). + Error("can not propagate jail: schedule event", + "cause", err, + "validator", consAddr.String()) + } + s.StakingKeeper.Jail(ctx, consAddr) +} + +// Unjail captures the unjail event and calls the decorated staking keeper unjail method +func (s StakingDecorator) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) { + if s.k.meshConsumer == nil { + s.StakingKeeper.Unjail(ctx, consAddr) + } + + val := s.StakingKeeper.ValidatorByConsAddr(ctx, consAddr) + if val == nil { + meshsecuritykeeper.ModuleLogger(ctx). + Error("can not propagate unjail: validator not found", "validator", consAddr.String()) + } else if err := s.k.meshConsumer.ScheduleUnjailed(ctx, val.GetOperator()); err != nil { + meshsecuritykeeper.ModuleLogger(ctx). + Error("can not propagate unjail: schedule event", + "cause", err, + "validator", consAddr.String()) + } + s.StakingKeeper.Unjail(ctx, consAddr) +} diff --git a/x/meshsecurityprovider/keeper/keeper.go b/x/meshsecurityprovider/keeper/keeper.go index b8815835..36afe777 100644 --- a/x/meshsecurityprovider/keeper/keeper.go +++ b/x/meshsecurityprovider/keeper/keeper.go @@ -25,11 +25,12 @@ type Keeper struct { bankKeeper types.BankKeeper wasmKeeper types.WasmKeeper stakingKeeper types.StakingKeeper + meshConsumer types.MeshSecurityConsumer } func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, authority string, bankKeeper types.BankKeeper, wasmKeeper types.WasmKeeper, - stakingKeeper types.StakingKeeper, + stakingKeeper types.StakingKeeper, meshConsumer types.MeshSecurityConsumer, ) *Keeper { return &Keeper{ storeKey: storeKey, @@ -38,6 +39,7 @@ func NewKeeper(cdc codec.BinaryCodec, storeKey storetypes.StoreKey, bankKeeper: bankKeeper, wasmKeeper: wasmKeeper, stakingKeeper: stakingKeeper, + meshConsumer: meshConsumer, } } diff --git a/x/meshsecurityprovider/keeper/params.go b/x/meshsecurityprovider/keeper/params.go index fe08d85f..b420c64b 100644 --- a/x/meshsecurityprovider/keeper/params.go +++ b/x/meshsecurityprovider/keeper/params.go @@ -12,4 +12,4 @@ func (k Keeper) VaultAddress(ctx sdk.Context) string { // NativeStakingAddress - Address of native staking contract func (k Keeper) NativeStakingAddress(ctx sdk.Context) string { return k.GetParams(ctx).NativeStakingAddress -} \ No newline at end of file +} diff --git a/x/meshsecurityprovider/keeper/wasm.go b/x/meshsecurityprovider/keeper/wasm.go new file mode 100644 index 00000000..5ebf4ecd --- /dev/null +++ b/x/meshsecurityprovider/keeper/wasm.go @@ -0,0 +1,31 @@ +package keeper + +import ( + "encoding/json" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/mesh-security-sdk/x/meshsecurityprovider/contract" +) + +// caller must ensure gas limits are set proper and handle panics +func (k Keeper) doSudoCall(ctx sdk.Context, contractAddr sdk.AccAddress, msg contract.SudoMsg) error { + bz, err := json.Marshal(msg) + if err != nil { + return errorsmod.Wrap(err, "marshal sudo msg") + } + _, err = k.wasmKeeper.Sudo(ctx, contractAddr, bz) + return err +} + +// SendJailHandlingMsg send jail handling message to contract via sudo +func (k Keeper) SendJailHandlingMsg(ctx sdk.Context, contractAddr sdk.AccAddress, jailed []contract.ValidatorAddr, tombstoned []contract.ValidatorAddr) error { + msg := contract.SudoMsg{ + Jailing: &contract.ValidatorSlash{ + Jailed: jailed, + Tombstoned: tombstoned, + }, + } + return k.doSudoCall(ctx, contractAddr, msg) +} diff --git a/x/meshsecurityprovider/types/expected_keeper.go b/x/meshsecurityprovider/types/expected_keeper.go index e714ae51..0e98c25d 100644 --- a/x/meshsecurityprovider/types/expected_keeper.go +++ b/x/meshsecurityprovider/types/expected_keeper.go @@ -17,6 +17,8 @@ type BankKeeper interface { } type WasmKeeper interface { + Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) + HasContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) bool QuerySmart(ctx sdk.Context, contractAddress sdk.AccAddress, queryMsg []byte) ([]byte, error) } @@ -27,3 +29,11 @@ type StakingKeeper interface { Unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, shares sdk.Dec) (amount math.Int, err error) Undelegate(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) (time.Time, error) } + +type MeshSecurityConsumer interface { + ScheduleUnjailed(ctx sdk.Context, addr sdk.ValAddress) error + ScheduleJailed(ctx sdk.Context, addr sdk.ValAddress) error + ScheduleTombstoned(ctx sdk.Context, addr sdk.ValAddress) error + ScheduleModified(ctx sdk.Context, addr sdk.ValAddress) error + ScheduleSlashed(ctx sdk.Context, addr sdk.ValAddress, power int64, height int64, totalSlashAmount math.Int, slashRatio sdk.Dec) error +}