Now that your module is ready, it can be incorporated in the ./app.go file, along with the other two modules auth and bank:
NOTE: Your application needs to import the code you just wrote. Here the import path is set to this repository (
github.com/cosmos/sdk-application-tutorial/x/nameservice). If you are following along in your own repo you will need to change the import path to reflect that (github.com/{ .Username }/{ .Project.Repo }/x/nameservice).
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/stake"
"github.com/cosmos/sdk-application-tutorial/x/nameservice"
bam "github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
tmtypes "github.com/tendermint/tendermint/types"
)Next you need to add the stores' keys as well as the Keepers in your nameserviceApp struct, and update the constructor accordingly
const (
appName = "nameservice"
)
type nameserviceApp struct {
*bam.BaseApp
cdc *codec.Codec
keyMain *sdk.KVStoreKey
keyAccount *sdk.KVStoreKey
keyNSnames *sdk.KVStoreKey
keyNSowners *sdk.KVStoreKey
keyNSprices *sdk.KVStoreKey
keyFeeCollection *sdk.KVStoreKey
accountKeeper auth.AccountKeeper
bankKeeper bank.Keeper
feeCollectionKeeper auth.FeeCollectionKeeper
nsKeeper nameservice.Keeper
}
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"),
keyNSnames: sdk.NewKVStoreKey("ns_names"),
keyNSowners: sdk.NewKVStoreKey("ns_owners"),
keyNSprices: sdk.NewKVStoreKey("ns_prices"),
keyFeeCollection: sdk.NewKVStoreKey("fee_collection"),
}
return app
}At this point, the constructor still lacks important logic. Namely, it needs to:
- Instantiate required
Keepersfrom each desired module. - Generate
storeKeysrequired by eachKeeper. - Register
Handlers from each module. TheAddRoute()method frombaseapp'srouteris used to this end. - Register
Queriers from each module. TheAddRoute()method frombaseapp'squeryRouteris used to this end. - Mount
KVStores to the provided keys in thebaseAppmultistore. - Set the
initChainerfor defining the initial application state.
Your finalized constructor should look like this:
// 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"),
keyNSnames: sdk.NewKVStoreKey("ns_names"),
keyNSowners: sdk.NewKVStoreKey("ns_owners"),
keyNSprices: sdk.NewKVStoreKey("ns_prices"),
keyFeeCollection: sdk.NewKVStoreKey("fee_collection"),
}
// The AccountKeeper handles address -> account lookups
app.accountKeeper = auth.NewAccountKeeper(
app.cdc,
app.keyAccount,
auth.ProtoBaseAccount,
)
// The BankKeeper allows you perform sdk.Coins interactions
app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper)
// 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.keyNSnames,
app.keyNSowners,
app.keyNSprices,
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))
// 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.keyNSnames,
app.keyNSowners,
app.keyNSprices,
)
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
}
return app
}The initChainer defines how accounts in genesis.json are mapped into the application state on initial chain start. The ExportAppStateAndValidators function helps bootstrap the initial state for application. You don't need to worry too much about either of these for now.
The constructor registers the initChainer function, but it isn't defined yet. Go ahead and create it:
// GenesisState represents chain state at the start of the chain. Any initial state (account balances) are stored here.
type GenesisState struct {
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)
}
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}
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
if err != nil {
return nil, nil, err
}
return appState, validators, err
}Finally add a helper function to generate an amino *codec.Codec that properly registers all of the modules used in your application:
// MakeCodec generates the necessary codecs for Amino
func MakeCodec() *codec.Codec {
var cdc = codec.New()
auth.RegisterCodec(cdc)
bank.RegisterCodec(cdc)
nameservice.RegisterCodec(cdc)
stake.RegisterCodec(cdc)
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
return cdc
}