multistore
to persist data and a router
to handle transactions.
Here is a simplified view of how transactions are handled by an application built on top of the Cosmos SDK when transferred from CometBFT via DeliverTx
:
- Decode
transactions
received from the CometBFT consensus engine (remember that CometBFT only deals with[]bytes
). - Extract
messages
fromtransactions
and do basic sanity checks. - Route each message to the appropriate module so that it can be processed.
- Commit state changes.
baseapp
baseapp
is the boilerplate implementation of a Cosmos SDK application. It comes with an implementation of the ABCI to handle the connection with the underlying consensus engine. Typically, a Cosmos SDK application extends baseapp
by embedding it in app.go
.
Here is an example of this from simapp
, the Cosmos SDK demonstration app:
Copy
Ask AI
/go:build app_v1
package simapp
import (
"encoding/json"
"fmt"
"io"
"maps"
abci "github.com/cometbft/cometbft/abci/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/cosmos/gogoproto/proto"
"github.com/spf13/cast"
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1"
"cosmossdk.io/client/v2/autocli"
clienthelpers "cosmossdk.io/client/v2/helpers"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/circuit"
circuitkeeper "cosmossdk.io/x/circuit/keeper"
circuittypes "cosmossdk.io/x/circuit/types"
"cosmossdk.io/x/evidence"
evidencekeeper "cosmossdk.io/x/evidence/keeper"
evidencetypes "cosmossdk.io/x/evidence/types"
"cosmossdk.io/x/feegrant"
feegrantkeeper "cosmossdk.io/x/feegrant/keeper"
feegrantmodule "cosmossdk.io/x/feegrant/module"
"cosmossdk.io/x/nft"
nftkeeper "cosmossdk.io/x/nft/keeper"
nftmodule "cosmossdk.io/x/nft/module"
"cosmossdk.io/x/tx/signing"
"cosmossdk.io/x/upgrade"
upgradekeeper "cosmossdk.io/x/upgrade/keeper"
upgradetypes "cosmossdk.io/x/upgrade/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/address"
"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/std"
testdata_pulsar "github.com/cosmos/cosmos-sdk/testutil/testdata/testpb"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
sigtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec"
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"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"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"
consensus "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"
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/epochs"
epochskeeper "github.com/cosmos/cosmos-sdk/x/epochs/keeper"
epochstypes "github.com/cosmos/cosmos-sdk/x/epochs/types"
"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/protocolpool"
protocolpoolkeeper "github.com/cosmos/cosmos-sdk/x/protocolpool/keeper"
protocolpooltypes "github.com/cosmos/cosmos-sdk/x/protocolpool/types"
"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"
)
const appName = "SimApp"
var (
/ DefaultNodeHome default home directories for the application daemon
DefaultNodeHome string
/ 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,
protocolpooltypes.ModuleName: nil,
protocolpooltypes.ProtocolPoolEscrowAccount: nil
}
)
var (
_ runtime.AppI = (*SimApp)(nil)
_ servertypes.Application = (*SimApp)(nil)
)
/ SimApp extends an ABCI application, but with most of its parameters exported.
/ They are exported for convenience in creating helper functions, as object
/ capabilities aren't needed for testing.
type SimApp 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
/ essential keepers
AccountKeeper authkeeper.AccountKeeper
BankKeeper bankkeeper.BaseKeeper
StakingKeeper *stakingkeeper.Keeper
SlashingKeeper slashingkeeper.Keeper
MintKeeper mintkeeper.Keeper
DistrKeeper distrkeeper.Keeper
GovKeeper govkeeper.Keeper
UpgradeKeeper *upgradekeeper.Keeper
EvidenceKeeper evidencekeeper.Keeper
ConsensusParamsKeeper consensusparamkeeper.Keeper
CircuitKeeper circuitkeeper.Keeper
/ supplementary keepers
FeeGrantKeeper feegrantkeeper.Keeper
GroupKeeper groupkeeper.Keeper
AuthzKeeper authzkeeper.Keeper
NFTKeeper nftkeeper.Keeper
EpochsKeeper epochskeeper.Keeper
ProtocolPoolKeeper protocolpoolkeeper.Keeper
/ the module manager
ModuleManager *module.Manager
BasicModuleManager module.BasicManager
/ simulation manager
sm *module.SimulationManager
/ module configurator
configurator module.Configurator
}
func init() {
var err error
DefaultNodeHome, err = clienthelpers.GetNodeHomeDirectory(".simapp")
if err != nil {
panic(err)
}
}
/ NewSimApp returns a reference to an initialized SimApp.
func NewSimApp(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
loadLatest bool,
appOpts servertypes.AppOptions,
baseAppOptions ...func(*baseapp.BaseApp),
) *SimApp {
interfaceRegistry, _ := types.NewInterfaceRegistryWithOptions(types.InterfaceRegistryOptions{
ProtoFiles: proto.HybridResolver,
SigningOptions: signing.Options{
AddressCodec: address.Bech32Codec{
Bech32Prefix: sdk.GetConfig().GetBech32AccountAddrPrefix(),
},
ValidatorAddressCodec: address.Bech32Codec{
Bech32Prefix: sdk.GetConfig().GetBech32ValidatorAddrPrefix(),
},
},
})
appCodec := codec.NewProtoCodec(interfaceRegistry)
legacyAmino := codec.NewLegacyAmino()
txConfig := tx.NewTxConfig(appCodec, tx.DefaultSignModes)
if err := interfaceRegistry.SigningContext().Validate(); err != nil {
panic(err)
}
std.RegisterLegacyAminoCodec(legacyAmino)
std.RegisterInterfaces(interfaceRegistry)
/ Below we could construct and set an application specific mempool and
/ ABCI 1.0 PrepareProposal and ProcessProposal handlers. These defaults are
/ already set in the SDK's BaseApp, this shows an example of how to override
/ them.
/
/ Example:
/
/ bApp := baseapp.NewBaseApp(...)
/ nonceMempool := mempool.NewSenderNonceMempool()
/ abciPropHandler := NewDefaultProposalHandler(nonceMempool, bApp)
/
/ bApp.SetMempool(nonceMempool)
/ bApp.SetPrepareProposal(abciPropHandler.PrepareProposalHandler())
/ bApp.SetProcessProposal(abciPropHandler.ProcessProposalHandler())
/
/ Alternatively, you can construct BaseApp options, append those to
/ baseAppOptions and pass them to NewBaseApp.
/
/ Example:
/
/ prepareOpt = func(app *baseapp.BaseApp) {
/ abciPropHandler := baseapp.NewDefaultProposalHandler(nonceMempool, app)
/ app.SetPrepareProposal(abciPropHandler.PrepareProposalHandler())
/
}
/ baseAppOptions = append(baseAppOptions, prepareOpt)
/ create and set dummy vote extension handler
voteExtOp := func(bApp *baseapp.BaseApp) {
voteExtHandler := NewVoteExtensionHandler()
voteExtHandler.SetHandlers(bApp)
}
baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution())
bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...)
bApp.SetCommitMultiStoreTracer(traceStore)
bApp.SetVersion(version.Version)
bApp.SetInterfaceRegistry(interfaceRegistry)
bApp.SetTxEncoder(txConfig.TxEncoder())
keys := storetypes.NewKVStoreKeys(
authtypes.StoreKey,
banktypes.StoreKey,
stakingtypes.StoreKey,
minttypes.StoreKey,
distrtypes.StoreKey,
slashingtypes.StoreKey,
govtypes.StoreKey,
consensusparamtypes.StoreKey,
upgradetypes.StoreKey,
feegrant.StoreKey,
evidencetypes.StoreKey,
circuittypes.StoreKey,
authzkeeper.StoreKey,
nftkeeper.StoreKey,
group.StoreKey,
epochstypes.StoreKey,
protocolpooltypes.StoreKey,
)
/ register streaming services
if err := bApp.RegisterStreamingServices(appOpts, keys); err != nil {
panic(err)
}
app := &SimApp{
BaseApp: bApp,
legacyAmino: legacyAmino,
appCodec: appCodec,
txConfig: txConfig,
interfaceRegistry: interfaceRegistry,
keys: keys,
}
/ set the BaseApp's parameter store
app.ConsensusParamsKeeper = consensusparamkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[consensusparamtypes.StoreKey]),
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
runtime.EventService{
},
)
bApp.SetParamStore(app.ConsensusParamsKeeper.ParamsStore)
/ add keepers
app.AccountKeeper = authkeeper.NewAccountKeeper(
appCodec,
runtime.NewKVStoreService(keys[authtypes.StoreKey]),
authtypes.ProtoBaseAccount,
maccPerms,
authcodec.NewBech32Codec(sdk.Bech32MainPrefix),
sdk.Bech32MainPrefix,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
authkeeper.WithUnorderedTransactions(true),
)
app.BankKeeper = bankkeeper.NewBaseKeeper(
appCodec,
runtime.NewKVStoreService(keys[banktypes.StoreKey]),
app.AccountKeeper,
BlockedAddresses(),
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
logger,
)
/ optional: enable sign mode textual by overwriting the default tx config (after setting the bank keeper)
enabledSignModes := append(tx.DefaultSignModes, sigtypes.SignMode_SIGN_MODE_TEXTUAL)
txConfigOpts := tx.ConfigOptions{
EnabledSignModes: enabledSignModes,
TextualCoinMetadataQueryFn: txmodule.NewBankKeeperCoinMetadataQueryFn(app.BankKeeper),
}
txConfig, err := tx.NewTxConfigWithOptions(
appCodec,
txConfigOpts,
)
if err != nil {
panic(err)
}
app.txConfig = txConfig
app.StakingKeeper = stakingkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[stakingtypes.StoreKey]),
app.AccountKeeper,
app.BankKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
authcodec.NewBech32Codec(sdk.Bech32PrefixValAddr),
authcodec.NewBech32Codec(sdk.Bech32PrefixConsAddr),
)
app.MintKeeper = mintkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[minttypes.StoreKey]),
app.StakingKeeper,
app.AccountKeeper,
app.BankKeeper,
authtypes.FeeCollectorName,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
/ mintkeeper.WithMintFn(mintkeeper.DefaultMintFn(minttypes.DefaultInflationCalculationFn)), custom mintFn can be added here
)
app.ProtocolPoolKeeper = protocolpoolkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[protocolpooltypes.StoreKey]),
app.AccountKeeper,
app.BankKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
app.DistrKeeper = distrkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[distrtypes.StoreKey]),
app.AccountKeeper,
app.BankKeeper,
app.StakingKeeper,
authtypes.FeeCollectorName,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
distrkeeper.WithExternalCommunityPool(app.ProtocolPoolKeeper),
)
app.SlashingKeeper = slashingkeeper.NewKeeper(
appCodec,
legacyAmino,
runtime.NewKVStoreService(keys[slashingtypes.StoreKey]),
app.StakingKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
app.FeeGrantKeeper = feegrantkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(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(),
),
)
app.CircuitKeeper = circuitkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[circuittypes.StoreKey]),
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
app.AccountKeeper.AddressCodec(),
)
app.BaseApp.SetCircuitBreaker(&app.CircuitKeeper)
app.AuthzKeeper = authzkeeper.NewKeeper(
runtime.NewKVStoreService(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,
runtime.NewKVStoreService(keys[upgradetypes.StoreKey]),
appCodec,
homePath,
app.BaseApp,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
/ 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)
govConfig := govtypes.DefaultConfig()
/*
Example of setting gov params:
govConfig.MaxMetadataLen = 10000
*/
govKeeper := govkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[govtypes.StoreKey]),
app.AccountKeeper,
app.BankKeeper,
app.StakingKeeper,
app.DistrKeeper,
app.MsgServiceRouter(),
govConfig,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
/ govkeeper.WithCustomCalculateVoteResultsAndVotingPowerFn(...), / Add if you want to use a custom vote calculation function.
)
/ Set legacy router for backwards compatibility with gov v1beta1
govKeeper.SetLegacyRouter(govRouter)
app.GovKeeper = *govKeeper.SetHooks(
govtypes.NewMultiGovHooks(
/ register the governance hooks
),
)
app.NFTKeeper = nftkeeper.NewKeeper(
runtime.NewKVStoreService(keys[nftkeeper.StoreKey]),
appCodec,
app.AccountKeeper,
app.BankKeeper,
)
/ create evidence keeper with router
evidenceKeeper := evidencekeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[evidencetypes.StoreKey]),
app.StakingKeeper,
app.SlashingKeeper,
app.AccountKeeper.AddressCodec(),
runtime.ProvideCometInfoService(),
)
/ If evidence needs to be handled for the app, set routes in router here and seal
app.EvidenceKeeper = *evidenceKeeper
app.EpochsKeeper = epochskeeper.NewKeeper(
runtime.NewKVStoreService(keys[epochstypes.StoreKey]),
appCodec,
)
app.EpochsKeeper.SetHooks(
epochstypes.NewMultiEpochHooks(
/ insert epoch hooks receivers here
),
)
/**** Module Options ****/
/ 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,
txConfig,
),
auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, nil),
vesting.NewAppModule(app.AccountKeeper, app.BankKeeper),
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper, nil),
feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry),
gov.NewAppModule(appCodec, &app.GovKeeper, app.AccountKeeper, app.BankKeeper, nil),
mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil, nil),
slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, nil, app.interfaceRegistry),
distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper, nil),
staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, nil),
upgrade.NewAppModule(app.UpgradeKeeper, app.AccountKeeper.AddressCodec()),
evidence.NewAppModule(app.EvidenceKeeper),
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),
circuit.NewAppModule(appCodec, app.CircuitKeeper),
epochs.NewAppModule(app.EpochsKeeper),
protocolpool.NewAppModule(app.ProtocolPoolKeeper, app.AccountKeeper, app.BankKeeper),
)
/ BasicModuleManager defines the module BasicManager is in charge of setting up basic,
/ non-dependant module elements, such as codec registration and genesis verification.
/ By default it is composed of all the module from the module manager.
/ Additionally, app module basics can be overwritten by passing them as argument.
app.BasicModuleManager = module.NewBasicManagerFromManager(
app.ModuleManager,
map[string]module.AppModuleBasic{
genutiltypes.ModuleName: genutil.NewAppModuleBasic(genutiltypes.DefaultMessageValidator),
govtypes.ModuleName: gov.NewAppModuleBasic(
[]govclient.ProposalHandler{
},
),
})
app.BasicModuleManager.RegisterLegacyAminoCodec(legacyAmino)
app.BasicModuleManager.RegisterInterfaces(interfaceRegistry)
/ NOTE: upgrade module is required to be prioritized
app.ModuleManager.SetOrderPreBlockers(
upgradetypes.ModuleName,
authtypes.ModuleName,
)
/ 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
app.ModuleManager.SetOrderBeginBlockers(
minttypes.ModuleName,
distrtypes.ModuleName,
protocolpooltypes.ModuleName,
slashingtypes.ModuleName,
evidencetypes.ModuleName,
stakingtypes.ModuleName,
genutiltypes.ModuleName,
authz.ModuleName,
epochstypes.ModuleName,
)
app.ModuleManager.SetOrderEndBlockers(
govtypes.ModuleName,
stakingtypes.ModuleName,
genutiltypes.ModuleName,
feegrant.ModuleName,
group.ModuleName,
protocolpooltypes.ModuleName,
)
/ 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.
genesisModuleOrder := []string{
authtypes.ModuleName,
banktypes.ModuleName,
distrtypes.ModuleName,
stakingtypes.ModuleName,
slashingtypes.ModuleName,
govtypes.ModuleName,
minttypes.ModuleName,
genutiltypes.ModuleName,
evidencetypes.ModuleName,
authz.ModuleName,
feegrant.ModuleName,
nft.ModuleName,
group.ModuleName,
upgradetypes.ModuleName,
vestingtypes.ModuleName,
consensusparamtypes.ModuleName,
circuittypes.ModuleName,
epochstypes.ModuleName,
protocolpooltypes.ModuleName,
}
exportModuleOrder := []string{
consensusparamtypes.ModuleName,
authtypes.ModuleName,
protocolpooltypes.ModuleName, / Must be exported before bank
banktypes.ModuleName,
distrtypes.ModuleName,
stakingtypes.ModuleName,
slashingtypes.ModuleName,
govtypes.ModuleName,
minttypes.ModuleName,
genutiltypes.ModuleName,
evidencetypes.ModuleName,
authz.ModuleName,
feegrant.ModuleName,
nft.ModuleName,
group.ModuleName,
upgradetypes.ModuleName,
vestingtypes.ModuleName,
circuittypes.ModuleName,
epochstypes.ModuleName,
}
app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...)
app.ModuleManager.SetOrderExportGenesis(exportModuleOrder...)
/ Uncomment if you want to set a custom migration order here.
/ app.ModuleManager.SetOrderMigrations(custom order)
app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter())
err = app.ModuleManager.RegisterServices(app.configurator)
if err != nil {
panic(err)
}
/ 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, nil),
}
app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, overrideModules)
app.sm.RegisterStoreDecoders()
/ initialize stores
app.MountKVStores(keys)
/ initialize BaseApp
app.SetInitChainer(app.InitChainer)
app.SetPreBlocker(app.PreBlocker)
app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)
app.setAnteHandler(txConfig)
/ 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
/
/ 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 {
panic(fmt.Errorf("error loading last version: %w", err))
}
}
return app
}
func (app *SimApp)
setAnteHandler(txConfig client.TxConfig) {
anteHandler, err := NewAnteHandler(
HandlerOptions{
ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
SignModeHandler: txConfig.SignModeHandler(),
FeegrantKeeper: app.FeeGrantKeeper,
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
SigVerifyOptions: []ante.SigVerificationDecoratorOption{
/ change below as needed.
ante.WithUnorderedTxGasCost(ante.DefaultUnorderedTxGasCost),
ante.WithMaxUnorderedTxTimeoutDuration(ante.DefaultMaxTimeoutDuration),
},
},
&app.CircuitKeeper,
},
)
if err != nil {
panic(err)
}
/ Set the AnteHandler for the app
app.SetAnteHandler(anteHandler)
}
func (app *SimApp)
setPostHandler() {
postHandler, err := posthandler.NewPostHandler(
posthandler.HandlerOptions{
},
)
if err != nil {
panic(err)
}
app.SetPostHandler(postHandler)
}
/ Name returns the name of the App
func (app *SimApp)
Name()
string {
return app.BaseApp.Name()
}
/ PreBlocker application updates every pre block
func (app *SimApp)
PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) {
return app.ModuleManager.PreBlock(ctx)
}
/ BeginBlocker application updates every begin block
func (app *SimApp)
BeginBlocker(ctx sdk.Context) (sdk.BeginBlock, error) {
return app.ModuleManager.BeginBlock(ctx)
}
/ EndBlocker application updates every end block
func (app *SimApp)
EndBlocker(ctx sdk.Context) (sdk.EndBlock, error) {
return app.ModuleManager.EndBlock(ctx)
}
func (a *SimApp)
Configurator()
module.Configurator {
return a.configurator
}
/ InitChainer application update at chain initialization
func (app *SimApp)
InitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
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 *SimApp)
LoadHeight(height int64)
error {
return app.LoadVersion(height)
}
/ LegacyAmino returns SimApp's 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 *SimApp)
LegacyAmino() *codec.LegacyAmino {
return app.legacyAmino
}
/ AppCodec returns SimApp's 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 *SimApp)
AppCodec()
codec.Codec {
return app.appCodec
}
/ InterfaceRegistry returns SimApp's InterfaceRegistry
func (app *SimApp)
InterfaceRegistry()
types.InterfaceRegistry {
return app.interfaceRegistry
}
/ TxConfig returns SimApp's TxConfig
func (app *SimApp)
TxConfig()
client.TxConfig {
return app.txConfig
}
/ AutoCliOpts returns the autocli options for the app.
func (app *SimApp)
AutoCliOpts()
autocli.AppOptions {
modules := make(map[string]appmodule.AppModule, 0)
for _, m := range app.ModuleManager.Modules {
if moduleWithName, ok := m.(module.HasName); ok {
moduleName := moduleWithName.Name()
if appModule, ok := moduleWithName.(appmodule.AppModule); ok {
modules[moduleName] = appModule
}
}
}
return autocli.AppOptions{
Modules: modules,
ModuleOptions: runtimeservices.ExtractAutoCLIOptions(app.ModuleManager.Modules),
AddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()),
ValidatorAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()),
ConsensusAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()),
}
}
/ DefaultGenesis returns a default genesis from the registered AppModuleBasic's.
func (a *SimApp)
DefaultGenesis()
map[string]json.RawMessage {
return a.BasicModuleManager.DefaultGenesis(a.appCodec)
}
/ GetKey returns the KVStoreKey for the provided store key.
/
/ NOTE: This is solely to be used for testing purposes.
func (app *SimApp)
GetKey(storeKey string) *storetypes.KVStoreKey {
return app.keys[storeKey]
}
/ GetStoreKeys returns all the stored store keys.
func (app *SimApp)
GetStoreKeys() []storetypes.StoreKey {
keys := make([]storetypes.StoreKey, 0, len(app.keys))
for _, key := range app.keys {
keys = append(keys, key)
}
return keys
}
/ SimulationManager implements the SimulationApp interface
func (app *SimApp)
SimulationManager() *module.SimulationManager {
return app.sm
}
/ RegisterAPIRoutes registers all application module routes with the provided
/ API server.
func (app *SimApp)
RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIConfig) {
clientCtx := apiSvr.ClientCtx
/ Register new tx routes from grpc-gateway.
authtx.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
/ Register new CometBFT queries routes from grpc-gateway.
cmtservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
/ Register node gRPC service for grpc-gateway.
nodeservice.RegisterGRPCGatewayRoutes(clientCtx, apiSvr.GRPCGatewayRouter)
/ Register grpc-gateway routes for all modules.
app.BasicModuleManager.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 *SimApp)
RegisterTxService(clientCtx client.Context) {
authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry)
}
/ RegisterTendermintService implements the Application.RegisterTendermintService method.
func (app *SimApp)
RegisterTendermintService(clientCtx client.Context) {
cmtApp := server.NewCometABCIWrapper(app)
cmtservice.RegisterTendermintService(
clientCtx,
app.BaseApp.GRPCQueryRouter(),
app.interfaceRegistry,
cmtApp.Query,
)
}
func (app *SimApp)
RegisterNodeService(clientCtx client.Context, cfg config.Config) {
nodeservice.RegisterNodeService(clientCtx, app.GRPCQueryRouter(), cfg)
}
/ GetMaccPerms returns a copy of the module account permissions
/
/ NOTE: This is solely to be used for testing purposes.
func GetMaccPerms()
map[string][]string {
return maps.Clone(maccPerms)
}
/ 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
}
baseapp
is to provide a secure interface between the store and the extensible state machine while defining as little about the state machine as possible (staying true to the ABCI).
For more on baseapp
, please click here.
Multistore
The Cosmos SDK provides amultistore
for persisting state. The multistore allows developers to declare any number of KVStores
. These KVStores
only accept the []byte
type as value and therefore any custom structure needs to be marshalled using a codec before being stored.
The multistore abstraction is used to divide the state in distinct compartments, each managed by its own module. For more on the multistore, click here
Modules
The power of the Cosmos SDK lies in its modularity. Cosmos SDK applications are built by aggregating a collection of interoperable modules. Each module defines a subset of the state and contains its own message/transaction processor, while the Cosmos SDK is responsible for routing each message to its respective module. Here is a simplified view of how a transaction is processed by the application of each full-node when it is received in a valid block: Each module can be seen as a little state-machine. Developers need to define the subset of the state handled by the module, as well as custom message types that modify the state (Note:messages
are extracted from transactions
by baseapp
). In general, each module declares its own KVStore
in the multistore
to persist the subset of the state it defines. Most developers will need to access other 3rd party modules when building their own modules. Given that the Cosmos SDK is an open framework, some of the modules may be malicious, which means there is a need for security principles to reason about inter-module interactions. These principles are based on object-capabilities. In practice, this means that instead of having each module keep an access control list for other modules, each module implements special objects called keepers
that can be passed to other modules to grant a pre-defined set of capabilities.
Cosmos SDK modules are defined in the x/
folder of the Cosmos SDK. Some core modules include:
x/auth
: Used to manage accounts and signatures.x/bank
: Used to enable tokens and token transfers.x/staking
+x/slashing
: Used to build Proof-of-Stake blockchains.
x/
, which anyone can use in their app, the Cosmos SDK lets you build your own custom modules. You can check an example of that in the tutorial.