# 引入你的模块并完成程序

现在你的模块已就绪,它可以和其它两个模块authbank被合并到./app.go文件中:

你的应用程序需要导入你刚编写的代码。这里导入路径设置为此存储库(github.com/cosmos/sdk-application-tutorial/nameservice/x/nameservice)。如果您是在自己的仓库中进行的前面的操作,则需要更改导入路径(github.com/{.Username}/{.Project.Repo}/x/nameservice)。

Copy package app import ( "encoding/json" "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/sdk-tutorials/nameservice/x/nameservice" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" tmos "github.com/tendermint/tendermint/libs/os" dbm "github.com/tendermint/tm-db" tmtypes "github.com/tendermint/tendermint/types" )

接下来,你需要在nameServiceApp结构体中添加存储的key和Keepers,并更新构造函数:

Copy const ( appName = "nameservice" ) type nameServiceApp struct { *bam.BaseApp cdc *codec.Codec keyMain *sdk.KVStoreKey keyAccount *sdk.KVStoreKey keyNS *sdk.KVStoreKey keyFeeCollection *sdk.KVStoreKey keyParams *sdk.KVStoreKey tkeyParams *sdk.TransientStoreKey accountKeeper auth.AccountKeeper bankKeeper bank.Keeper feeCollectionKeeper auth.FeeCollectionKeeper paramsKeeper params.Keeper nsKeeper nameservice.Keeper } // NewNameServiceApp is a constructor function for nameServiceApp func NewNameServiceApp(logger log.Logger, db dbm.DB) *nameServiceApp { // First define the top level codec that will be shared by the different modules cdc := MakeCodec() // BaseApp handles interactions with Tendermint through the ABCI protocol bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc)) // Here you initialize your application with the store keys it requires var app = &nameServiceApp{ BaseApp: bApp, cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), keyNS: sdk.NewKVStoreKey("ns"), keyFeeCollection: sdk.NewKVStoreKey("fee_collection"), keyParams: sdk.NewKVStoreKey("params"), tkeyParams: sdk.NewTransientStoreKey("transient_params"), } return app }

此时,构造函数仍然缺少重要的逻辑。它需要:

  • 从每个所需模块中实例化所需的Keeper
  • 生成每个Keeper所需的storeKey
  • 注册每个模块的handlerbaseapp路由器AddRoute() 方法用来做这个。
  • 注册每个模块的querierbaseappqueryRouter中的AddRoute()方法用来做这个。
  • KVStores挂载到baseApp的multistore提供的key值。
  • 设置initChainer来定义初始应用程序状态。

你最终的构造函数应该如下所示:

Copy // NewNameServiceApp is a constructor function for nameServiceApp func NewNameServiceApp(logger log.Logger, db dbm.DB) *nameServiceApp { // First define the top level codec that will be shared by the different modules cdc := MakeCodec() // BaseApp handles interactions with Tendermint through the ABCI protocol bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc)) // Here you initialize your application with the store keys it requires var app = &nameServiceApp{ BaseApp: bApp, cdc: cdc, keyMain: sdk.NewKVStoreKey("main"), keyAccount: sdk.NewKVStoreKey("acc"), keyNS: sdk.NewKVStoreKey("ns"), keyFeeCollection: sdk.NewKVStoreKey("fee_collection"), keyParams: sdk.NewKVStoreKey("params"), tkeyParams: sdk.NewTransientStoreKey("transient_params"), } // The ParamsKeeper handles parameter storage for the application app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams) // The AccountKeeper handles address -> account lookups app.accountKeeper = auth.NewAccountKeeper( app.cdc, app.keyAccount, app.paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount, ) // The BankKeeper allows you perform sdk.Coins interactions app.bankKeeper = bank.NewBaseKeeper( app.accountKeeper, app.paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, ) // The FeeCollectionKeeper collects transaction fees and renders them to the fee distribution module app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(cdc, app.keyFeeCollection) // The NameserviceKeeper is the Keeper from the module for this tutorial // It handles interactions with the namestore app.nsKeeper = nameservice.NewKeeper( app.bankKeeper, app.keyNS, app.cdc, ) // The AnteHandler handles signature verification and transaction pre-processing app.SetAnteHandler(auth.NewAnteHandler(app.accountKeeper, app.feeCollectionKeeper)) // The app.Router is the main transaction router where each module registers its routes // Register the bank and nameservice routes here app.Router(). AddRoute("bank", bank.NewHandler(app.bankKeeper)). AddRoute("nameservice", nameservice.NewHandler(app.nsKeeper)) // The app.QueryRouter is the main query router where each module registers its routes app.QueryRouter(). AddRoute("nameservice", nameservice.NewQuerier(app.nsKeeper)). AddRoute("acc", auth.NewQuerier(app.accountKeeper)) // The initChainer handles translating the genesis.json file into initial state for the network app.SetInitChainer(app.initChainer) app.MountStores( app.keyMain, app.keyAccount, app.keyNS, app.keyFeeCollection, app.keyParams, app.tkeyParams, ) err := app.LoadLatestVersion(app.keyMain) if err != nil { tmos.Exit(err.Error()) } return app }

注意:上面提到的 TransientStore 是 KVStore 的内存实现,用于未持久化的状态。

initChainer定义了genesis.json中的帐户如何在初始化区块链时被映射到应用程序状态。ExportAppStateAndValidators函数可帮助引导初始化应用程序的状态。你现在不需要太关心它们。

构造函数注册了initChainer函数,但尚未定义。继续创建它:

Copy // GenesisState represents chain state at the start of the chain. Any initial state (account balances) are stored here. type GenesisState struct { AuthData auth.GenesisState `json:"auth"` BankData bank.GenesisState `json:"bank"` Accounts []*auth.BaseAccount `json:"accounts"` } func (app *nameServiceApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { stateJSON := req.AppStateBytes genesisState := new(GenesisState) err := app.cdc.UnmarshalJSON(stateJSON, genesisState) if err != nil { panic(err) } for _, acc := range genesisState.Accounts { acc.AccountNumber = app.accountKeeper.GetNextAccountNumber(ctx) app.accountKeeper.SetAccount(ctx, acc) } auth.InitGenesis(ctx, app.accountKeeper, app.feeCollectionKeeper, genesisState.AuthData) bank.InitGenesis(ctx, app.bankKeeper, genesisState.BankData) return abci.ResponseInitChain{} } // ExportAppStateAndValidators does the things func (app *nameServiceApp) ExportAppStateAndValidators() (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { ctx := app.NewContext(true, abci.Header{}) accounts := []*auth.BaseAccount{} appendAccountsFn := func(acc auth.Account) bool { account := &auth.BaseAccount{ Address: acc.GetAddress(), Coins: acc.GetCoins(), } accounts = append(accounts, account) return false } app.accountKeeper.IterateAccounts(ctx, appendAccountsFn) genState := GenesisState{ Accounts: accounts, AuthData: auth.DefaultGenesisState(), BankData: bank.DefaultGenesisState(), } appState, err = codec.MarshalJSONIndent(app.cdc, genState) if err != nil { return nil, nil, err } return appState, validators, err }

最后添加一个辅助函数来生成一个animo--*codec.Codec,它可以正确地注册你应用程序中使用的所有模块:

Copy // MakeCodec generates the necessary codecs for Amino func MakeCodec() *codec.Codec { var cdc = codec.New() auth.RegisterCodec(cdc) bank.RegisterCodec(cdc) nameservice.RegisterCodec(cdc) staking.RegisterCodec(cdc) sdk.RegisterCodec(cdc) codec.RegisterCrypto(cdc) return cdc }

# 现在您已经创建了一个包含模块的应用程序,现在是时候构建入口点了