Cosmos-sdkのチュートリアルを触ってみる その6

Takuya Fujita
Aug 9 · 17 min read

前回の続きを書いていきます。前回までのリンクは以下の通りです。

気がついたらリンクの数だけでも結構な量になってしまいました。
結構な量になってしまいますが、あとちょっとです!!

前回まででapp.goに対して、storeに接続するためのkeyとKeeperの設定を行ないました。Keeperの定義の続きをやっていきます。

前回までだとコンストラクターに重要なものが抜けていました。今から、

・必要なモジュールから必要なKeeperのインスタンス化
・それぞれのKeeperが必要とするstoreKeysの生成
・それぞれのモジュールからのHandlerの登録
・それぞれのモジュールからQuerierの登録
・baseAppのマルチストアに提供されているkeyへのKVStoreのマウント
・アプリケーションの初期ステートを定義するためのinitChainerを設定

をやっていくみたいです。マウントって接続先に対して、存在を認識させることなんですね。

コンストラクタを以下のように書き換えていきます。前回同様app.goファイルです。
かなり長くなっています。上の項目を意識しながら読んでいきましょう。

// 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. Note: Codec will be explained later
cdc := MakeCodec()
// BaseApp handles interactions with Tendermint through the ABCI protocol
bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc))
// Here your initialize your application with the store keys it requires
var app = &nameServiceApp{
BaseApp: bApp,
cdc: cdc,
keyMain: sdk.NewKVStoreKey(bam.MainStoreKey),
keyAccount: sdk.NewKVStoreKey(auth.StoreKey),
keyFeeCollection: sdk.NewKVStoreKey(auth.FeeStoreKey),
keyStaking: sdk.NewKVStoreKey(staking.StoreKey),
tkeyStaking: sdk.NewTransientStoreKey(staking.TStoreKey),
keyDistr: sdk.NewKVStoreKey(distr.StoreKey),
tkeyDistr: sdk.NewTransientStoreKey(distr.TStoreKey),
keyNS: sdk.NewKVStoreKey(params.StoreKey),
keyPrams: sdk.NewKVStoreKey(params.StoreKey),
tkeyParams: sdk.NewTransientStoreKey(params.TStoreKey),
keySlashing: sdk.NewKVStoreKey(slashing.StoreKey),
}
// The ParamsKeeper handles parameter storage for the application
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams, app.tkeyParams, params.DefaultCodespace)
// Set specific subspaces
authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace)
bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace)
stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultPramspace)
distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace)
slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace)
// The AccountKeeper handles address -> account lookups
app.accountKeeper = auth.NewAccountKeeper(
app.cdc,
app.keyAccount,
authSubspace,
auth.ProtoBaseAccount,
)
// TheBankKeeper allows you perform sdk.Coins interactions
app.bankKeeper = bank.NewBaseKeeper(
app.accountKeeper,
banksubspace,
bank.DefaultCodespace,
)
// The FeeCollectionKeeper collects transaction fees and renders them to the fee distribution module
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(cdc, app.keyFeeCollection)
// The staking keeper
stakingKeeper := staking.NewKeeper( //ここのイコール違うのきになる
app.cdc,
app.keyStaking,
app.tkeyStaking,
app.bankKeeper,
stakingSubspace,
staking.DefaultCodespace,
)
app.distrKeeper = distr.NewKeeper(
app.cdc,
app.keyDistr,
distrSubspace,
app.bankKeeper,
&stakingKeeper,
app.feeCollectionKeeper,
distr.DefaultCodespace,
)
app.slashingKeeper = slashing.NewKeeper(
app.cdc,
app.keySlashing,
&stakingKeeper,
slashingSubspace,
slashing.DefaultCodespace,
)
// register the staking hooks
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
app.stakingKeeper = *stakingKeeper.setHooks(
staking.NewMultiStakingHooks(
app.distrKeeper.Hooks(),
app.slashingKeeper.Hooks()), //ここのきになる
)
// 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,
)
app.mm = module.NewManager(
genaccounts.NewAppModule(app.accountKeeper),
genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx),
auth.NewAppModule(app.accountKeeper, app.feeCollectionKeeper),
bank.NewAppModule(app.bankKeeper, app.accountKeeper),
nameservice.NewAppModule(app.nsKeeper, app.bankKeeper),
distr.NewAppModule(app.distrKeeper),
slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper),
staking.NewAppModule(app.stakingKeeper, app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper),
)
app.mm.SetOrderBeginBlockers(distr.ModuleName, slashing.ModuleName)
app.mm.SetOrderEndBlockers(staking.ModuleName)
// Sets the order of Genesis - Order maters, genutil is to always come last
app.mm.SetOrderInitGenesis(
genaccounts.ModuleName,
distr.ModuleName,
staking.ModuleName,
auth.Modulename,
bank.ModuleName,
slashing.ModuleName,
nameservice.ModuleName,
genutil.ModuleName,
)
// register all module routes and module queriers
app.mm.RegisterRoutes(app.Router(), app.QueryRouter())
// The initChainer handles translating the genesis.json file into initial state for the network
app.SetInitChainer(app.InitChainer)
app.SetBeginBlocker(app.BeginBlocker)
app.SetEndBlocker(app.EndBlocker)
// The AnteHandler handles signature verification and transaction pre-processing
app.SetAnteHandler(
auth.NewAnteHandler(
app.accountKeeper,
app.feeCollectionKeeper,
auth.DefaultSigVerificationGasConsumer,
),
)
app.MountStores(
app.keyMain,
app.keyAccount,
app.keyFeeCollection,
app.keyStaking,
app.tkeyStaking,
app.keyDistr,
app.tkeyDistr,
app.keySlashing,
app.keyNS,
app.keyParams,
app.tkeyParams,
)
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
}
return app
}

長い長い!!一つの関数が長すぎる!!

まず、storeKeysの生成を行なってその後、各Keeperのインスタンス化を行なっているのが確認できると思います。

app.SetInitChainer()関数では、genesis.jsonというステートに関するファイルが、チェーン開始時にちゃんとアプリケーションのステートに反映される方法を定義しています。次に追記するExportAppStateAndValidators()関数は、アプリケーションの初期状態のブートストラップを行うのに役立ちます。

今の所、これら二つの関数についてあまり気にする必要はありません。
また他にもBeginBlocker,EndBlocker,LoadHeightといういくつかのメソッドを追記していく必要があります。久しぶりにブロックチェーンっぽいワードが出てきていますね。

コンストラクタではinitChainer()関数を宣言していますが、まだ定義されていません。作成して行きましょう。

// GenesisState represents chain state at the start of the chain. Any initial state (account balances) are stored here.
type Genesis State map[string]json.RawMessage
func NewDefaultGenesisState() GenesisState {
return ModuleBasics.DefaultGenesis()
}
func (app *nameServiceApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
var genesisState GenesisState
err := app.cdc.UnmarshalJSON(req.AppStateBytes, &genesisState)
if err != nil {
panic(err)
}
return app.mm.InitGenesis(ctx, genesisState)
}
func (app *nameServiceApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
return app.mm.BeginBlock(ctx, req)
}
func (app *nameServiceApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
return app.mm.EndBlock(ctx, req)
}
func (app *nameServiceApp) LoadHeight(height int64) error {
return app.LoadVersion(height, app.keyMain)
}
//_________________________________________________________func (app *nameServiceApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string,
) (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
// as if they could withdraw from the start of the next block
ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()})
genState := app.mm.ExportGenesis(ctx)
appState, err = codec.MarshalJSONIndent(app.cdc, genState)
if err != nil {
return nil, nil, err
}
validators = staking.WriteValidators(ctx, app.stakingKeeper)return appState, validators, nil
}

最後に、amino形式のcodecを行うための関数を加えます。

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

これで、モジュールを含め、アプリケーション部分は全て作成し終えたので、次回からは、それらを動かすためのエントリーポイントを作成して行きます!

今回結構短くなりましたが、キリが悪いのでここまでとします。その5とその6で1セットという感じになります。

お知らせ

■HashHubでは入居者募集中です!
HashHubは、ブロックチェーン業界で働いている人のためのコワーキングスペースを運営しています。ご利用をご検討の方は、下記のWEBサイトからお問い合わせください。また、最新情報はTwitterで発信中です。

HashHub:https://hashhub.tokyo/
Twitter:https://twitter.com/HashHub_Tokyo

■ブロックチェーンエンジニア集中講座開講中!
HashHubではブロックチェーンエンジニアを育成するための短期集中講座を開講しています。お申込み、詳細は下記のページをご覧ください。

ブロックチェーンエンジニア集中講座:https://www.blockchain-edu.jp/

Blockchain Engineer Blog

This Blog is for all Blockchain Engineer

Takuya Fujita

Written by

Blockchain Engineer Blog

This Blog is for all Blockchain Engineer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade