cosmos主网即将上线,对文档做了大量更新。特地翻译了一下,方便小伙伴们阅览, 之后会持续更新
第三章教程:
- 开始
- 程序目标
- 开始编写你的程序
- Keeper
- Msg和Handler
- SetName
- BuyName
- Querier
- Codec文件
- Nameservice模块的CLI
- nameservice模块的REST接口
- 引入你的模块并完成程序
- Entrypoint
- 编译你的程序
- 编译并运行程序
- 运行REST路由
引入你的模块并完成程序
现在你的模块已就绪,它可以和其它两个模块auth
和bank
被合并到./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
。 - 注册每个模块的
handler
。baseapp
的路由器
的AddRoute()方法用来做这个。 - 注册每个模块的
querier
。baseapp
的queryRouter
中的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
}