CosmosSDK, BaseApp and Modules

This article is about to explain how CosmosSDK works together with ABCI. Or in other words, how the built-in modules works.

BaseApp

It is the base App which implemets the ABCI intefrcae, and actually hold all modules.

// BaseApp reflects the ABCI application implementation.
type BaseApp struct { // nolint: maligned
	// initialized on creation
	logger      log.Logger
	name        string               // application name from abci.Info
	db          dbm.DB               // common DB backend
	cms         sdk.CommitMultiStore // Main (uncached) state
	storeLoader StoreLoader          // function to handle store loading, may be overridden with SetStoreLoader()
	router      sdk.Router           // handle any kind of message
	queryRouter sdk.QueryRouter      // router for redirecting query calls
	txDecoder   sdk.TxDecoder        // unmarshal []byte into sdk.Tx

	// set upon LoadVersion or LoadLatestVersion.
	baseKey *sdk.KVStoreKey // Main KVStore in cms

	anteHandler    sdk.AnteHandler  // ante handler for fee and auth
	initChainer    sdk.InitChainer  // initialize state with validators and state blob
	beginBlocker   sdk.BeginBlocker // logic to run before any txs
	endBlocker     sdk.EndBlocker   // logic to run after all txs, and to determine valset changes
	addrPeerFilter sdk.PeerFilter   // filter peers by address and port
	idPeerFilter   sdk.PeerFilter   // filter peers by node ID
	fauxMerkleMode bool             // if true, IAVL MountStores uses MountStoresDB for simulation speed.

	// volatile states:
	//
	// checkState is set on InitChain and reset on Commit
	// deliverState is set on InitChain and BeginBlock and set to nil on Commit
	checkState   *state // for CheckTx
	deliverState *state // for DeliverTx

	// an inter-block write-through cache provided to the context during deliverState
	interBlockCache sdk.MultiStorePersistentCache

	// absent validators from begin block
	voteInfos []abci.VoteInfo

	// consensus params
	// TODO: Move this in the future to baseapp param store on main store.
	consensusParams *abci.ConsensusParams

	// The minimum gas prices a validator is willing to accept for processing a
	// transaction. This is mainly used for DoS and spam prevention.
	minGasPrices sdk.DecCoins

	// flag for sealing options and parameters to a BaseApp
	sealed bool

	// block height at which to halt the chain and gracefully shutdown
	haltHeight uint64

	// minimum block time (in Unix seconds) at which to halt the chain and gracefully shutdown
	haltTime uint64

	// application's version string
	appVersion string
}
  • db, DB backend
  • cms, main state
  • router, handles messages, TXs
  • initChainer, initialize state with validators and state blob
  • beginBlocker, logic to run before any txs
  • endBlocker, logic to run after all txs, and to determine valset changes

ABCI

BaseApp implements the ABCI:

// Application is an interface that enables any finite, deterministic state machine
// to be driven by a blockchain-based replication engine via the ABCI.
// All methods take a RequestXxx argument and return a ResponseXxx argument,
// except CheckTx/DeliverTx, which take `tx []byte`, and `Commit`, which takes nothing.
type Application interface {
	// Info/Query Connection
	Info(RequestInfo) ResponseInfo                // Return application info
	SetOption(RequestSetOption) ResponseSetOption // Set application option
	Query(RequestQuery) ResponseQuery             // Query for state

	// Mempool Connection
	CheckTx(RequestCheckTx) ResponseCheckTx // Validate a tx for the mempool

	// Consensus Connection
	InitChain(RequestInitChain) ResponseInitChain    // Initialize blockchain w validators/other info from TendermintCore
	BeginBlock(RequestBeginBlock) ResponseBeginBlock // Signals the beginning of a block
	DeliverTx(RequestDeliverTx) ResponseDeliverTx    // Deliver a tx for full processing
	EndBlock(RequestEndBlock) ResponseEndBlock       // Signals the end of a block, returns changes to the validator set
	Commit() ResponseCommit                          // Commit the state and return the application Merkle root hash
}

beginBlocker/endBlocker/initChainer

These callbacks will be invoked by BaseApp when ABCI interface methods get invoked. Actually they will ask the module manager to perform the actual stuff.

// BeginBlocker application updates every begin block
func (app *SimApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
	return app.mm.BeginBlock(ctx, req)
}

// EndBlocker application updates every end block
func (app *SimApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
	return app.mm.EndBlock(ctx, req)
}

// InitChainer application update at chain initialization
func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
	var genesisState GenesisState
	app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState)
	return app.mm.InitGenesis(ctx, genesisState)
}

router

BaseApp will ask module manage to generate the routing for all underlying modules. Therefore App could pass the message/tx to them:

func (m *Manager) RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter) {
	for _, module := range m.Modules {
		if module.Route() != "" {
			router.AddRoute(module.Route(), module.NewHandler())
		}
		if module.QuerierRoute() != "" {
			queryRouter.AddRoute(module.QuerierRoute(), module.NewQuerierHandler())
		}
	}
}

runTx

This is invoked from BaseApp.CheckTx/DeliverTx to actually performance the TXs.

runTx will call runMsg which passes the Tx to the router and then the AppModules.


// runTx processes a transaction within a given execution mode, encoded transaction
// bytes, and the decoded transaction itself. All state transitions occur through
// a cached Context depending on the mode provided. State only gets persisted
// if all messages get executed successfully and the execution mode is DeliverTx.
// Note, gas execution info is always returned. A reference to a Result is
// returned if the tx does not run out of gas and if all the messages are valid
// and execute successfully. An error is returned otherwise.
func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (gInfo sdk.GasInfo, result *sdk.Result, err error) {
	...
	// Create a new Context based off of the existing Context with a cache-wrapped
	// MultiStore in case message processing fails. At this point, the MultiStore
	// is doubly cached-wrapped.
	runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes)

	// Attempt to execute all messages and only update state if all messages pass
	// and we're in DeliverTx. Note, runMsgs will never return a reference to a
	// Result if any single message fails or does not have a registered Handler.
	result, err = app.runMsgs(runMsgCtx, msgs, mode)
	if err == nil && mode == runTxModeDeliver {
		msCache.Write()
	}

	return gInfo, result, err
}

// runMsgs iterates through a list of messages and executes them with the provided
// Context and execution mode. Messages will only be executed during simulation
// and DeliverTx. An error is returned if any single message fails or if a
// Handler does not exist for a given message route. Otherwise, a reference to a
// Result is returned. The caller must not commit state if an error is returned.
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*sdk.Result, error) {
	msgLogs := make(sdk.ABCIMessageLogs, 0, len(msgs))
	data := make([]byte, 0, len(msgs))
	events := sdk.EmptyEvents()

	// NOTE: GasWanted is determined by the AnteHandler and GasUsed by the GasMeter.
	for i, msg := range msgs {
		// skip actual execution for (Re)CheckTx mode
		if mode == runTxModeCheck || mode == runTxModeReCheck {
			break
		}

		msgRoute := msg.Route()
		handler := app.router.Route(ctx, msgRoute)
		if handler == nil {
			return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s; message index: %d", msgRoute, i)
		}

		msgResult, err := handler(ctx, msg)
		if err != nil {
			return nil, sdkerrors.Wrapf(err, "failed to execute message; message index: %d", i)
		}

		msgEvents := sdk.Events{
			sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, msg.Type())),
		}
		msgEvents = msgEvents.AppendEvents(msgResult.Events)

		// append message events, data and logs
		//
		// Note: Each message result's data must be length-prefixed in order to
		// separate each result.
		events = events.AppendEvents(msgEvents)
		data = append(data, msgResult.Data...)
		msgLogs = append(msgLogs, sdk.NewABCIMessageLog(uint16(i), msgResult.Log, msgEvents))
	}

	return &sdk.Result{
		Data:   data,
		Log:    strings.TrimSpace(msgLogs.String()),
		Events: events,
	}, nil
}

AppModule

Below is copied from code, to exlain what AppModule is.

/*
Package module contains application module patterns and associated "manager" functionality.
The module pattern has been broken down by:
 - independent module functionality (AppModuleBasic)
 - inter-dependent module genesis functionality (AppModuleGenesis)
 - inter-dependent module simulation functionality (AppModuleSimulation)
 - inter-dependent module full functionality (AppModule)

inter-dependent module functionality is module functionality which somehow
depends on other modules, typically through the module keeper.  Many of the
module keepers are dependent on each other, thus in order to access the full
set of module functionality we need to define all the keepers/params-store/keys
etc. This full set of advanced functionality is defined by the AppModule interface.

Independent module functions are separated to allow for the construction of the
basic application structures required early on in the application definition
and used to enable the definition of full module functionality later in the
process. This separation is necessary, however we still want to allow for a
high level pattern for modules to follow - for instance, such that we don't
have to manually register all of the codecs for all the modules. This basic
procedure as well as other basic patterns are handled through the use of
BasicManager.

Lastly the interface for genesis functionality (AppModuleGenesis) has been
separated out from full module functionality (AppModule) so that modules which
are only used for genesis can take advantage of the Module patterns without
needlessly defining many placeholder functions
*/
// AppModule is the standard form for an application module
type AppModule interface {
	AppModuleGenesis

	// registers
	RegisterInvariants(sdk.InvariantRegistry)

	// routes
	Route() string
	NewHandler() sdk.Handler
	QuerierRoute() string
	NewQuerierHandler() sdk.Querier

	// ABCI
	BeginBlock(sdk.Context, abci.RequestBeginBlock)
	EndBlock(sdk.Context, abci.RequestEndBlock) []abci.ValidatorUpdate
}

// AppModuleGenesis is the standard form for an application module genesis functions
type AppModuleGenesis interface {
	AppModuleBasic
	InitGenesis(sdk.Context, json.RawMessage) []abci.ValidatorUpdate
	ExportGenesis(sdk.Context) json.RawMessage
}

// AppModule is the standard form for an application module
type AppModule interface {
	AppModuleGenesis

	// registers
	RegisterInvariants(sdk.InvariantRegistry)

	// routes
	Route() string
	NewHandler() sdk.Handler
	QuerierRoute() string
	NewQuerierHandler() sdk.Querier

	// ABCI
	BeginBlock(sdk.Context, abci.RequestBeginBlock)
	EndBlock(sdk.Context, abci.RequestEndBlock) []abci.ValidatorUpdate
}

Manager

The module manager defines a module manager that provides the high level utility for managing and executing operations for a group of modules.

type Manager struct {
	Modules            map[string]AppModule
	OrderInitGenesis   []string
	OrderExportGenesis []string
	OrderBeginBlockers []string
	OrderEndBlockers   []string
}

Thus, modules are registered to the manager:

  app.mm = module.NewManager(
    genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx),
    auth.NewAppModule(app.accountKeeper),
    bank.NewAppModule(app.bankKeeper, app.accountKeeper),
    crisis.NewAppModule(&app.crisisKeeper),
    supply.NewAppModule(app.supplyKeeper, app.accountKeeper),
    gov.NewAppModule(app.govKeeper, app.accountKeeper, app.supplyKeeper),
    mint.NewAppModule(app.mintKeeper),
    slashing.NewAppModule(app.slashingKeeper, app.accountKeeper, app.stakingKeeper),
    distr.NewAppModule(app.distrKeeper, app.accountKeeper, app.supplyKeeper, app.stakingKeeper),
    // TODO: Add your module(s)
    staking.NewAppModule(app.stakingKeeper, app.accountKeeper, app.supplyKeeper),
    upgrade.NewAppModule(app.upgradeKeeper),
    evidence.NewAppModule(app.evidenceKeeper),
  )

Module Manager also provides some methods to better manage the modules:

  • SetOrderBeginBlockers, sets the order of set begin-blocker calls
  • SetOrderEndBlockers, sets the order of set end-blocker calls
  • SetOrderInitGenesis
  • SetOrderExportGenesis
  • RegisterInvariants
  • RegisterRoutes, registers all module routes and module querier routes

ABCI Call Stack

BeginBlock

github.com/cosmos/cosmos-sdk/types/module.(*Manager).BeginBlock at module.go:297
github.com/xjw/ttt/app.(*NewApp).BeginBlocker at app.go:385
github.com/xjw/ttt/app.(*NewApp).BeginBlocker-fm at app.go:384
github.com/cosmos/cosmos-sdk/baseapp.(*BaseApp).BeginBlock at abci.go:136
github.com/tendermint/tendermint/abci/client.(*localClient).BeginBlockSync at local_client.go:231
github.com/tendermint/tendermint/proxy.(*appConnConsensus).BeginBlockSync at app_conn.go:69
github.com/tendermint/tendermint/state.execBlockOnProxyApp at execution.go:280
github.com/tendermint/tendermint/state.(*BlockExecutor).ApplyBlock at execution.go:131
github.com/tendermint/tendermint/consensus.(*State).finalizeCommit at state.go:1431
github.com/tendermint/tendermint/consensus.(*State).tryFinalizeCommit at state.go:1350
github.com/tendermint/tendermint/consensus.(*State).enterCommit.func1 at state.go:1285
github.com/tendermint/tendermint/consensus.(*State).enterCommit at state.go:1322
github.com/tendermint/tendermint/consensus.(*State).addVote at state.go:1822
github.com/tendermint/tendermint/consensus.(*State).tryAddVote at state.go:1642
github.com/tendermint/tendermint/consensus.(*State).handleMsg at state.go:709
github.com/tendermint/tendermint/consensus.(*State).receiveRoutine at state.go:660
runtime.goexit at asm_amd64.s:1357
 - Async stack trace
github.com/tendermint/tendermint/consensus.(*State).OnStart at state.go:335

EndBlock

github.com/cosmos/cosmos-sdk/types/module.(*Manager).EndBlock at module.go:309
github.com/xjw/ttt/app.(*NewApp).EndBlocker at app.go:390
github.com/xjw/ttt/app.(*NewApp).EndBlocker-fm at app.go:389
github.com/cosmos/cosmos-sdk/baseapp.(*BaseApp).EndBlock at abci.go:151
github.com/tendermint/tendermint/abci/client.(*localClient).EndBlockSync at local_client.go:239
github.com/tendermint/tendermint/proxy.(*appConnConsensus).EndBlockSync at app_conn.go:77
github.com/tendermint/tendermint/state.execBlockOnProxyApp at execution.go:300
github.com/tendermint/tendermint/state.(*BlockExecutor).ApplyBlock at execution.go:131
github.com/tendermint/tendermint/consensus.(*State).finalizeCommit at state.go:1431
github.com/tendermint/tendermint/consensus.(*State).tryFinalizeCommit at state.go:1350
github.com/tendermint/tendermint/consensus.(*State).enterCommit.func1 at state.go:1285
github.com/tendermint/tendermint/consensus.(*State).enterCommit at state.go:1322
github.com/tendermint/tendermint/consensus.(*State).addVote at state.go:1822
github.com/tendermint/tendermint/consensus.(*State).tryAddVote at state.go:1642
github.com/tendermint/tendermint/consensus.(*State).handleMsg at state.go:709
github.com/tendermint/tendermint/consensus.(*State).receiveRoutine at state.go:660
runtime.goexit at asm_amd64.s:1357
 - Async stack trace
github.com/tendermint/tendermint/consensus.(*State).OnStart at state.go:335

Commit

github.com/cosmos/cosmos-sdk/baseapp.(*BaseApp).Commit at abci.go:229
github.com/tendermint/tendermint/abci/client.(*localClient).CommitSync at local_client.go:215
github.com/tendermint/tendermint/proxy.(*appConnConsensus).CommitSync at app_conn.go:81
github.com/tendermint/tendermint/state.(*BlockExecutor).Commit at execution.go:212
github.com/tendermint/tendermint/state.(*BlockExecutor).ApplyBlock at execution.go:166
github.com/tendermint/tendermint/consensus.(*State).finalizeCommit at state.go:1431
github.com/tendermint/tendermint/consensus.(*State).tryFinalizeCommit at state.go:1350
github.com/tendermint/tendermint/consensus.(*State).enterCommit.func1 at state.go:1285
github.com/tendermint/tendermint/consensus.(*State).enterCommit at state.go:1322
github.com/tendermint/tendermint/consensus.(*State).addVote at state.go:1822
github.com/tendermint/tendermint/consensus.(*State).tryAddVote at state.go:1642
github.com/tendermint/tendermint/consensus.(*State).handleMsg at state.go:709
github.com/tendermint/tendermint/consensus.(*State).receiveRoutine at state.go:660
runtime.goexit at asm_amd64.s:1357
 - Async stack trace
github.com/tendermint/tendermint/consensus.(*State).OnStart at state.go:335

CheckTx

github.com/cosmos/cosmos-sdk/baseapp.(*BaseApp).runMsgs at baseapp.go:652
github.com/cosmos/cosmos-sdk/baseapp.(*BaseApp).runTx at baseapp.go:630
github.com/cosmos/cosmos-sdk/baseapp.(*BaseApp).CheckTx at abci.go:182
github.com/tendermint/tendermint/abci/client.(*localClient).CheckTxAsync at local_client.go:99
github.com/tendermint/tendermint/proxy.(*appConnMempool).CheckTxAsync at app_conn.go:114
github.com/tendermint/tendermint/mempool.(*CListMempool).CheckTx at clist_mempool.go:281
github.com/tendermint/tendermint/rpc/core.BroadcastTxSync at mempool.go:37
runtime.call64 at asm_amd64.s:540
reflect.Value.call at value.go:460
reflect.Value.Call at value.go:321
github.com/tendermint/tendermint/rpc/lib/server.makeJSONRPCHandler.func1 at http_json_handler.go:103
github.com/tendermint/tendermint/rpc/lib/server.handleInvalidJSONRPCPaths.func1 at http_json_handler.go:127
net/http.HandlerFunc.ServeHTTP at server.go:2007
net/http.(*ServeMux).ServeHTTP at server.go:2387
github.com/tendermint/tendermint/rpc/lib/server.maxBytesHandler.ServeHTTP at http_server.go:210
<autogenerated>:2
github.com/tendermint/tendermint/rpc/lib/server.RecoverAndLogHandler.func1 at http_server.go:183
net/http.HandlerFunc.ServeHTTP at server.go:2007
net/http.serverHandler.ServeHTTP at server.go:2802
net/http.(*conn).serve at server.go:1890
runtime.goexit at asm_amd64.s:1357
 - Async stack trace
net/http.(*Server).Serve at server.go:2928

CheckTx will not do the real execution stuff.

// skip actual execution for (Re)CheckTx mode
if mode == runTxModeCheck || mode == runTxModeReCheck {
	break
}

DeliverTx

SendCoin callstack

github.com/cosmos/cosmos-sdk/x/bank/internal/keeper.BaseSendKeeper.SendCoins at keeper.go:233
<autogenerated>:2
github.com/cosmos/cosmos-sdk/x/bank.handleMsgSend at handler.go:38
github.com/cosmos/cosmos-sdk/x/bank.NewHandler.func1 at handler.go:17
github.com/cosmos/cosmos-sdk/baseapp.(*BaseApp).runMsgs at baseapp.go:661
github.com/cosmos/cosmos-sdk/baseapp.(*BaseApp).runTx at baseapp.go:630
github.com/cosmos/cosmos-sdk/baseapp.(*BaseApp).DeliverTx at abci.go:207
github.com/tendermint/tendermint/abci/client.(*localClient).DeliverTxAsync at local_client.go:88
github.com/tendermint/tendermint/proxy.(*appConnConsensus).DeliverTxAsync at app_conn.go:73
github.com/tendermint/tendermint/state.execBlockOnProxyApp at execution.go:293
github.com/tendermint/tendermint/state.(*BlockExecutor).ApplyBlock at execution.go:131
github.com/tendermint/tendermint/consensus.(*State).finalizeCommit at state.go:1431
github.com/tendermint/tendermint/consensus.(*State).tryFinalizeCommit at state.go:1350
github.com/tendermint/tendermint/consensus.(*State).enterCommit.func1 at state.go:1285
github.com/tendermint/tendermint/consensus.(*State).enterCommit at state.go:1322
github.com/tendermint/tendermint/consensus.(*State).addVote at state.go:1822
github.com/tendermint/tendermint/consensus.(*State).tryAddVote at state.go:1642
github.com/tendermint/tendermint/consensus.(*State).handleMsg at state.go:709
github.com/tendermint/tendermint/consensus.(*State).receiveRoutine at state.go:660
runtime.goexit at asm_amd64.s:1357
 - Async stack trace
github.com/tendermint/tendermint/consensus.(*State).OnStart at state.go:335
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值