Cosmos-- 三.教程 -- 12.引入你的模块并完成程序

cosmos主网即将上线,对文档做了大量更新。特地翻译了一下,方便小伙伴们阅览, 之后会持续更新

第三章教程:

  1. 开始
  2. 程序目标
  3. 开始编写你的程序
  4. Keeper
  5. Msg和Handler
  6. SetName
  7. BuyName
  8. Querier
  9. Codec文件
  10. Nameservice模块的CLI
  11. nameservice模块的REST接口
  12. 引入你的模块并完成程序
  13. Entrypoint
  14. 编译你的程序
  15. 编译并运行程序
  16. 运行REST路由

引入你的模块并完成程序

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

你的应用程序需要导入你刚编写的代码。这里导入路径设置为此存储库(github.com/cosmos/sdk-application-tutorial/x/nameservice)。如果您是在自己的仓库中进行的前面的操作,则需要更改导入路径(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/params"
    "github.com/cosmos/cosmos-sdk/x/staking"
    "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"
)

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

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
    keyParams        *sdk.KVStoreKey
    tkeyParams       *sdk.TransientStoreKey

    accountKeeper       auth.AccountKeeper
    bankKeeper          bank.Keeper
    feeCollectionKeeper auth.FeeCollectionKeeper
    paramsKeeper        params.Keeper
    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"),
        keyParams:        sdk.NewKVStoreKey("params"),
        tkeyParams:       sdk.NewTransientStoreKey("transient_params"),
    }

  return app
}

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

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

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

// 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"),
        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.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,
        app.keyFeeCollection,
        app.keyParams,
        app.tkeyParams,
    )

    err := app.LoadLatestVersion(app.keyMain)
    if err != nil {
        cmn.Exit(err.Error())
    }

    return app
}

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

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

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

// 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,它可以正确地注册你应用程序中使用的所有模块:

// 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
}

你可能感兴趣的:(Cosmos-- 三.教程 -- 12.引入你的模块并完成程序)