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

Takuya Fujita
GBEC Tech Blog
Published in
17 min readAug 9, 2019

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

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

前回までで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/

--

--