剖析Cosmos SDK(十四):构建模块(不变量、模块genesis、模块接口)

煤油灯科技2022-07-28 10:00

不变量

不变量是应用程序的一个属性,它应该始终为真。在Cosmos SDK的context中,不变量是检查特定不变量的函数。这些功能有助于及早检测错误并对其采取行动以限制其潜在后果(例如通过停止链)。它们在应用程序的开发过程中也很有用,可以通过模拟检测错误。

实现不变量

不变量是检查模块中特定不变量的函数。 模块不变量必须遵循不变量类型:

package types

import "fmt"

// An Invariant is a function which tests a particular invariant.
// The invariant returns a descriptive message about what happened
// and a boolean indicating whether the invariant has been broken.
// The simulator will then halt and print the logs.
type Invariant func(ctx Context) (string, bool)

// Invariants defines a group of invariants
type Invariants []Invariant

// expected interface for registering invariants
type InvariantRegistry interface {
	RegisterRoute(moduleName, route string, invar Invariant)
}

// FormatInvariant returns a standardized invariant message.
func FormatInvariant(module, name, msg string) string {
	return fmt.Sprintf("%s: %s invariant\n%s\n", module, name, msg)
}

字符串返回值是不变量消息,打印日志时可以使用,布尔返回值是不变量检查的实际结果。

在实践中,每个模块都在模块文件夹中的keeper/invariants.go文件中实现不变量。标准是使用以下模型为每个不变量的逻辑分组实现一个不变量函数:Copy //检查余额相关不变量的不变量示例func BalanceInvariants(k Keeper) sdk.Invariant { return func(ctx sdk.Context) ( string, bool) { //实现对余额相关不变量的检查 } }:

// Example for an Invariant that checks balance-related invariants

func BalanceInvariants(k Keeper) sdk.Invariant {
	return func(ctx sdk.Context) (string, bool) {
        // Implement checks for balance-related invariants
    }
}

此外,模块开发人员通常应该实现一个AllInvariants函数来运行模块的所有Invariants函数:

// AllInvariants runs all invariants of the module.
// In this example, the module implements two Invariants: BalanceInvariants and DepositsInvariants

func AllInvariants(k Keeper) sdk.Invariant {

	return func(ctx sdk.Context) (string, bool) {
		res, stop := BalanceInvariants(k)(ctx)
		if stop {
			return res, stop
		}

		return DepositsInvariant(k)(ctx)
	}
}

最后,模块开发人员需要实现RegisterInvariants方法作为AppModule接口的一部分。 实际上,在module/module.go文件中实现的模块的RegisterInvariants方法通常只将调用延迟到在keeper/invariants.go文件中实现的RegisterInvariants方法。RegisterInvariants方法为InvariantRegistry中的每个Invariant函数注册一个路由:

func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
	ir.RegisterRoute(types.ModuleName, "module-accounts",
		ModuleAccountInvariants(k))
	ir.RegisterRoute(types.ModuleName, "nonnegative-power",
		NonNegativePowerInvariant(k))
	ir.RegisterRoute(types.ModuleName, "positive-delegation",
		PositiveDelegationInvariant(k))
	ir.RegisterRoute(types.ModuleName, "delegator-shares",
		DelegatorSharesInvariant(k))
}

不变注册表

InvariantRegistry是注册应用程序所有模块的不变量的注册表。每个应用程序只有一个InvariantRegistry,这意味着模块开发人员在构建模块时不需要实现自己的InvariantRegistry。所有模块开发人员需要做的就是在InvariantRegistry中注册他们模块的不变量,如上一节所述。本节的其余部分提供有关InvariantRegistry本身的更多信息,并且不包含与模块开发人员直接相关的任何内容。

InvariantRegistry的核心是在Cosmos SDK中定义为一个接口:

// expected interface for registering invariants
type InvariantRegistry interface {
	RegisterRoute(moduleName, route string, invar Invariant)
}

通常,此接口在特定模块的keeper中实现。InvariantRegistry最常用的实现可以在危机模块中找到:

// RegisterRoute register the routes for each of the invariants
func (k *Keeper) RegisterRoute(moduleName, route string, invar sdk.Invariant) {
	invarRoute := types.NewInvarRoute(moduleName, route, invar)
	k.routes = append(k.routes, invarRoute)
}

因此,InvariantRegistry通常通过在应用程序的构造函数中实例化危机模块的keeper来实例化。

可以通过消息手动检查不变量,但大多数情况下,它们会在每个块的末尾自动检查。以下是来自危机模块的示例:

// check all registered invariants
func EndBlocker(ctx sdk.Context, k keeper.Keeper) {
	defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker)

	if k.InvCheckPeriod() == 0 || ctx.BlockHeight()%int64(k.InvCheckPeriod()) != 0 {
		// skip running the invariant check
		return
	}
	k.AssertInvariants(ctx)
}

在这两种情况下,如果其中一个不变量返回false,则InvariantRegistry可以触发特殊逻辑(例如,让应用程序恐慌并在日志中打印不变量消息)。


模块genesis

模块通常处理状态的子集,因此,它们需要定义创世文件的相关子集以及初始化、验证和导出它的方法。

类型定义

从给定模块定义的genesis状态子集通常在 genesis.proto 文件中定义(有关如何定义 protobuf 消息的更多信息)。 定义模块的genesis状态子集的结构通常称为 GenesisState 并包含在genesis过程中需要初始化的所有模块相关值。

syntax = "proto3";
package cosmos.auth.v1beta1;

import "google/protobuf/any.proto";
import "gogoproto/gogo.proto";
import "cosmos/auth/v1beta1/auth.proto";

option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types";

// GenesisState defines the auth module's genesis state.
message GenesisState {
  // params defines all the paramaters of the module.
  Params params = 1 [(gogoproto.nullable) = false];

  // accounts are the accounts present at genesis.
  repeated google.protobuf.Any accounts = 2;
}

接下来,我们将介绍需要由模块开发人员实现的主要genesis相关方法,以便他们的模块在Cosmos SDK应用程序中使用。

DefaultGenesis

DefaultGenesis()方法是一个简单的方法,它使用每个参数的默认值调用GenesisState的构造函数。请参阅auth模块中的示例:

// DefaultGenesis returns default genesis state as raw bytes for the auth
// module.
func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
	return cdc.MustMarshalJSON(types.DefaultGenesisState())
}

ValidateGenesis

调用ValidateGenesis(data GenesisState)方法来验证提供的genesisState是否正确。它应该对GenesisState中列出的每个参数执行有效性检查。请参阅auth模块中的示例:

// ValidateGenesis performs basic validation of auth genesis data returning an
// error for any failed validation criteria.
func ValidateGenesis(data GenesisState) error {
	if err := data.Params.Validate(); err != nil {
		return err
	}

	genAccs, err := UnpackAccounts(data.Accounts)
	if err != nil {
		return err
	}

	return ValidateGenAccounts(genAccs)
}

其他genesis方法

除了与GenesisState直接相关的方法之外,模块开发人员还需要实现另外两种方法作为AppModuleGenesis接口的一部分(仅当模块需要在genesis中初始化状态子集时)。这些方法是InitGenesis和ExportGenesis。

InitGenesis

InitGenesis方法在应用程序首次启动时在InitChain期间执行。给定一个GenesisState,它通过对GenesisState中的每个参数使用模块的keeper setter函数来初始化模块管理的状态子集。

应用程序的模块管理器负责依次调用应用程序各个模块的InitGenesis方法。此顺序由应用程序开发人员通过管理器的 SetOrderGenesisMethod设置,该方法在应用程序的构造函数中调用。

查看auth模块中的InitGenesis示例:

// InitGenesis - Init store state from genesis data
//
// CONTRACT: old coins from the FeeCollectionKeeper need to be transferred through
// a genesis port script to the new fee collector account
func (ak AccountKeeper) InitGenesis(ctx sdk.Context, data types.GenesisState) {
	ak.SetParams(ctx, data.Params)

	accounts, err := types.UnpackAccounts(data.Accounts)
	if err != nil {
		panic(err)
	}
	accounts = types.SanitizeGenesisAccounts(accounts)

	for _, a := range accounts {
		acc := ak.NewAccount(ctx, a)
		ak.SetAccount(ctx, acc)
	}

	ak.GetModuleAccount(ctx, types.FeeCollectorName)
}

ExportGenesis

每当导出状态时,都会执行ExportGenesis方法。它采用模块管理的状态子集的最新已知版本,并从中创建一个新的GenesisState。 这主要用于需要通过硬分叉升级链时。

请参阅auth模块中的ExportGenesis示例:

// ExportGenesis returns a GenesisState for a given context and keeper
func (ak AccountKeeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
	params := ak.GetParams(ctx)

	var genAccounts types.GenesisAccounts
	ak.IterateAccounts(ctx, func(account types.AccountI) bool {
		genAccount := account.(types.GenesisAccount)
		genAccounts = append(genAccounts, genAccount)
		return false
	})

	return types.NewGenesisState(params, genAccounts)
}

模块接口

本文详细介绍了如何为模块构建CLI 和 REST 接口。包括来自各种Cosmos SDK模块的示例。

CLI

应用程序的主要界面之一是命令行界面。此入口点添加来自应用程序模块的命令,使最终用户能够创建包装在事务和查询中的消息。CLI 文件通常位于模块的./client/cli文件夹中。

交易指令

为了创建触发状态更改的消息,最终用户必须创建包装和传递消息的交易。交易指令创建包含一条或多条消息的事务。

交易命令通常有自己的tx.go文件,该文件位于模块的./client/cli文件夹中。命令在getter函数中指定,函数名称应包含命令名称。

这是来自x/bank模块的示例:

// NewSendTxCmd returns a CLI command handler for creating a MsgSend transaction.
func NewSendTxCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "send [from_key_or_address] [to_address] [amount]",
		Short: "Send funds from one account to another.",
		Long: `Send funds from one account to another.
Note, the '--from' flag is ignored as it is implied from [from_key_or_address].
When using '--dry-run' a key name cannot be used, only a bech32 address.
`,
		Args: cobra.ExactArgs(3),
		RunE: func(cmd *cobra.Command, args []string) error {
			cmd.Flags().Set(flags.FlagFrom, args[0])
			clientCtx, err := client.GetClientTxContext(cmd)
			if err != nil {
				return err
			}

			toAddr, err := sdk.AccAddressFromBech32(args[1])
			if err != nil {
				return err
			}

			coins, err := sdk.ParseCoinsNormalized(args[2])
			if err != nil {
				return err
			}

			msg := types.NewMsgSend(clientCtx.GetFromAddress(), toAddr, coins)

			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
		},
	}

	flags.AddTxFlagsToCmd(cmd)

	return cmd
}

在示例中,NewSendTxCmd()为包装和传递MsgSend的交易创建并返回交易命令。MsgSend 是用于将令牌从一个帐户发送到另一个帐户的消息。

一般来说,getter函数会执行以下操作:

  • 构建命令:阅读Cobra文档,了解更多关于如何创建命令的详细信息。
  • 使用:指定调用命令所需的用户输入的格式。在上面的例子中,send是交易命令的名称,[from_key_or_address], [to_address], 和[amount]是参数。
  • Args:用户提供的参数的数量。在本例中,正好有三个:[from_key_or_address],[to_address],和[amount]。
  • 短和长:命令的描述。希望有一个简短的描述。长描述可以用来提供额外的信息,当用户添加--help标志时,会显示出来。
  • RunE:定义了一个可以返回错误的函数。这是命令执行时被调用的函数,这个函数封装了创建一个新交易的所有逻辑。

该函数通常从获取clientCtx开始,这可以通过client.GetClientTxContext(cmd)来完成。clientCtx包含与交易处理相关的信息,包括关于用户的信息。在这个例子中,clientCtx被用来通过调用clientCtx.GetFromAddress()来检索发送者的地址。

如果适用,命令的参数会被解析。在这个例子中,参数[to_address]和[amount]都被解析了。

使用解析过的参数和来自clientCtx的信息创建一个消息。消息类型的构造函数被直接调用。在这种情况下,type.NewMsgSend(fromAddr, toAddr, amount)。在广播消息之前调用msg.ValidateBasic()和其他验证方法是很好的做法。

根据用户的要求,交易要么离线生成,要么签署并使用tx.GenerateOrBroadcastTxCLI(clientCtx, flags, msg)签名并广播到预配置的节点。

  • 添加交易标志:所有交易命令必须添加一组交易标志。交易标志用于从用户那里收集额外的信息(例如,用户愿意支付的费用数额)。使用AddTxFlagsToCmd(cmd)将交易标志添加到构建的命令中。
  • 返回命令。最后,交易命令被返回。

每个模块都必须实现NewTxCmd(),该模块聚合了该模块的所有交易命令。下面是x/bank模块的一个例子:

// NewTxCmd returns a root CLI command handler for all x/bank transaction commands.
func NewTxCmd() *cobra.Command {
	txCmd := &cobra.Command{
		Use:                        types.ModuleName,
		Short:                      "Bank transaction subcommands",
		DisableFlagParsing:         true,
		SuggestionsMinimumDistance: 2,
		RunE:                       client.ValidateCmd,
	}

	txCmd.AddCommand(
		NewSendTxCmd(),
		NewMultiSendTxCmd(),
	)

	return txCmd
}

每个模块还必须为AppModuleBasic实现GetTxCmd()方法,该方法只返回NewTxCmd()。 这允许root命令轻松地聚合每个模块的所有交易命令。 这是一个例子:

// GetTxCmd returns the root tx command for the bank module.
func (AppModuleBasic) GetTxCmd() *cobra.Command {
	return cli.NewTxCmd()
}

查询命令

查询允许用户收集有关应用程序或网络状态的信息;它们由应用程序路由并由定义它们的模块处理。查询命令通常在模块的 ./client/cli 文件夹中有自己的query.go文件。与交易命令一样,它们在getter函数中指定。以下是来自x/auth模块的查询命令示例:

// GetAccountCmd returns a query account that will display the state of the
// account at a given address.
func GetAccountCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "account [address]",
		Short: "Query for account by address",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			clientCtx, err := client.GetClientQueryContext(cmd)
			if err != nil {
				return err
			}
			key, err := sdk.AccAddressFromBech32(args[0])
			if err != nil {
				return err
			}

			queryClient := types.NewQueryClient(clientCtx)
			res, err := queryClient.Account(cmd.Context(), &types.QueryAccountRequest{Address: key.String()})
			if err != nil {
				node, err2 := clientCtx.GetNode()
				if err2 != nil {
					return err2
				}
				status, err2 := node.Status(context.Background())
				if err2 != nil {
					return err2
				}
				catchingUp := status.SyncInfo.CatchingUp
				if !catchingUp {
					return errors.Wrapf(err, "your node may be syncing, please check node status using `/status`")
				}
				return err
			}

			return clientCtx.PrintProto(res.Account)
		},
	}

	flags.AddQueryFlagsToCmd(cmd)

	return cmd
}

在示例中,GetAccountCmd()创建并返回一个查询命令,该命令根据提供的帐户地址返回帐户的状态。

通常,getter函数执行以下操作:

构建命令:阅读Cobra文档了解更多关于如何创建命令的详细信息。

使用:指定调用命令时需要的用户输入格式。在上面的例子中,account是查询命令的名称,[address]是参数。

Args:用户提供的参数数量。在这个例子中,正好有一个:[地址]。

短和长:命令的描述。希望有一个简短的描述。长描述可以用来提供额外的信息,当用户添加--help标志时,会显示出来。

RunE:定义了一个可以返回错误的函数。这是命令执行时被调用的函数。这个函数封装了创建一个新查询的所有逻辑。

该函数通常从获取clientCtx开始,这可以通过client.GetClientQueryContext(cmd)来完成。clientCtx包含与查询处理相关的信息。

如果适用,命令的参数会被解析。在这个例子中,参数[address]被解析了。

使用NewQueryClient(clientCtx)初始化一个新的queryClient。然后,queryClient被用来调用适当的查询。

clientCtx.PrintProto方法被用来格式化proto.Message对象,以便将结果打印回给用户。

  • 添加查询标志:所有查询命令必须添加一组查询标志。使用AddQueryFlagsToCmd(cmd)将查询标志添加到构造的命令中。
  • 返回命令:最后,查询命令被返回。

每个模块都必须实现GetQueryCmd(),它聚合了该模块的所有查询命令。下面是x/auth模块的一个例子。

typeHash   = "hash"
	typeAccSeq = "acc_seq"
	typeSig    = "signature"

	eventFormat = "{eventType}.{eventAttribute}={value}"
)

// GetQueryCmd returns the transaction commands for this module
func GetQueryCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:                        types.ModuleName,
		Short:                      "Querying commands for the auth module",
		DisableFlagParsing:         true,
		SuggestionsMinimumDistance: 2,
		RunE:                       client.ValidateCmd,
	}

	cmd.AddCommand(

每个模块还必须为AppModuleBasic实现GetQueryCmd()方法,该方法返回GetQueryCmd()函数。这允许root命令轻松聚合每个模块的所有查询命令。这是一个例子:

// GetQueryCmd returns the transaction commands for this module
func GetQueryCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:                        types.ModuleName,
		Short:                      "Querying commands for the auth module",
		DisableFlagParsing:         true,
		SuggestionsMinimumDistance: 2,
		RunE:                       client.ValidateCmd,
	}

	cmd.AddCommand(
		GetAccountCmd(),
		GetAccountsCmd(),
		QueryParamsCmd(),
		QueryModuleAccountsCmd(),
	)

	return cmd
}

标志

标志允许用户自定义命令。--fees和--gas-prices是允许用户为其交易设置费用和汽油价格的标志示例。

特定于模块的标志通常在模块的./client/cli文件夹中的flags.go文件中创建。创建标志时,开发人员设置值类型、标志名称、默认值和有关标志的描述。开发人员还可以选择根据需要标记标志,以便在用户不包含标志值时引发错误。

这是一个将--from标志添加到命令的示例:

cmd.Flags().String(FlagFrom, "", "Name or address of private key with which to sign")

在这个例子中,flag的值是一个String,flag的名字是from(FlagFrom常量的值),flag的默认值是“ ”,并且有一个描述,当出现a时会显示用户将--help添加到命令中。

这是一个根据需要标记--from 标志的示例:

cmd.MarkFlagRequired(FlagFrom)

有关创建标志的更多详细信息,请访问Cobra文档。

如交易命令中所述,所有交易命令都必须添加一组标志。 这是通过Cosmos SDK的./client/flags包中定义的AddTxFlagsToCmd方法完成的。

// AddTxFlagsToCmd adds common flags to a module tx command.
func AddTxFlagsToCmd(cmd *cobra.Command) {
	cmd.Flags().StringP(tmcli.OutputFlag, "o", "json", "Output format (text|json)")
	cmd.Flags().String(FlagKeyringDir, "", "The client Keyring directory; if omitted, the default 'home' directory will be used")
	cmd.Flags().String(FlagFrom, "", "Name or address of private key with which to sign")
	cmd.Flags().Uint64P(FlagAccountNumber, "a", 0, "The account number of the signing account (offline mode only)")
	cmd.Flags().Uint64P(FlagSequence, "s", 0, "The sequence number of the signing account (offline mode only)")
	cmd.Flags().String(FlagNote, "", "Note to add a description to the transaction (previously --memo)")
	cmd.Flags().String(FlagFees, "", "Fees to pay along with transaction; eg: 10uatom")
	cmd.Flags().String(FlagGasPrices, "", "Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom)")
	cmd.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
	cmd.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
	cmd.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ")
	cmd.Flags().StringP(FlagBroadcastMode, "b", BroadcastSync, "Transaction broadcasting mode (sync|async|block)")
	cmd.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible)")
	cmd.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name)")
	cmd.Flags().Bool(FlagOffline, false, "Offline mode (does not allow any online functionality)")
	cmd.Flags().BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation")
	cmd.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test|memory)")
	cmd.Flags().String(FlagSignMode, "", "Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature")
	cmd.Flags().Uint64(FlagTimeoutHeight, 0, "Set a block timeout height to prevent the tx from being committed past a certain height")
	cmd.Flags().String(FlagFeePayer, "", "Fee payer pays fees for the transaction instead of deducting from the signer")
	cmd.Flags().String(FlagFeeGranter, "", "Fee granter grants fees for the transaction")
	cmd.Flags().String(FlagTip, "", "Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux")
	cmd.Flags().Bool(FlagAux, false, "Generate aux signer data instead of sending a tx")

	// --gas can accept integers and "auto"
	cmd.Flags().String(FlagGas, "", fmt.Sprintf("gas limit to set per-transaction; set to %q to calculate sufficient gas automatically (default %d)", GasFlagAuto, DefaultGasLimit))
}

由于AddTxFlagsToCmd(cmd *cobra.Command)包含交易命令所需的所有基本标志,模块开发人员可以选择不添加他们自己的任何标志(指定参数通常可能更合适)。

类似地,有一个AddQueryFlagsToCmd(cmd *cobra.Command) 可以将常用标志添加到模块查询命令中。

// AddQueryFlagsToCmd adds common flags to a module query command.
func AddQueryFlagsToCmd(cmd *cobra.Command) {
	cmd.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to Tendermint RPC interface for this chain")
	cmd.Flags().Int64(FlagHeight, 0, "Use a specific height to query state at (this can error if the node is pruning state)")
	cmd.Flags().StringP(tmcli.OutputFlag, "o", "text", "Output format (text|json)")

	// some base commands does not require chainID e.g `simd testnet` while subcommands do
	// hence the flag should not be required for those commands
	_ = cmd.MarkFlagRequired(FlagChainID)
}

gRPC

gRPC是一个远程程序调用(RPC)框架。RPC是钱包和交易所等外部客户与区块链互动的首选方式。

除了提供ABCI查询途径外,Cosmos SDK还提供了一个gRPC代理服务器,将gRPC查询请求转为ABCI查询请求。

为了做到这一点,模块必须在AppModuleBasic上实现RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux),将客户端的gRPC请求连接到模块内的正确处理程序。

下面是来自x/auth模块的例子:

// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the auth module.
func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
	if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil {
		panic(err)
	}
}

gRPC-网关 REST

应用程序需要支持使用HTTP请求的Web服务(例如Keplr等Web钱包)。grpc-gateway将REST调用转换为gRPC调用,这对于不使用gRPC的客户端可能很有用。

想要公开REST查询的模块应该将google.api.http注释添加到它们的rpc方法中,例如下面来自x/auth模块的示例:

// Query defines the gRPC querier service.
service Query {
  // Accounts returns all the existing accounts
  //
  // Since: cosmos-sdk 0.43
  rpc Accounts(QueryAccountsRequest) returns (QueryAccountsResponse) {
    option (google.api.http).get = "/cosmos/auth/v1beta1/accounts";
  }

  // Account returns account details based on address.
  rpc Account(QueryAccountRequest) returns (QueryAccountResponse) {
    option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}";
  }

  // Params queries all parameters.
  rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
    option (google.api.http).get = "/cosmos/auth/v1beta1/params";
  }

  // ModuleAccounts returns all the existing module accounts.
  //
  // Since: cosmos-sdk 0.46
  rpc ModuleAccounts(QueryModuleAccountsRequest) returns (QueryModuleAccountsResponse) {
    option (google.api.http).get = "/cosmos/auth/v1beta1/module_accounts";
  }

  // Bech32Prefix queries bech32Prefix
  //
  // Since: cosmos-sdk 0.46
  rpc Bech32Prefix(Bech32PrefixRequest) returns (Bech32PrefixResponse) {
    option (google.api.http).get = "/cosmos/auth/v1beta1/bech32";
  }

  // AddressBytesToString converts Account Address bytes to string
  //
  // Since: cosmos-sdk 0.46
  rpc AddressBytesToString(AddressBytesToStringRequest) returns (AddressBytesToStringResponse) {
    option (google.api.http).get = "/cosmos/auth/v1beta1/bech32/{address_bytes}";
  }

  // AddressStringToBytes converts Address string to bytes
  //
  // Since: cosmos-sdk 0.46
  rpc AddressStringToBytes(AddressStringToBytesRequest) returns (AddressStringToBytesResponse) {
    option (google.api.http).get = "/cosmos/auth/v1beta1/bech32/{address_string}";
  }
}

gRPC网关与应用程序和Tendermint一起在进程中启动。可以通过在app.toml中设置gRPC配置启用来启用或禁用它。

Cosmos SDK提供了一个用于生成Swagger文档的命令 (protoc-gen-swagger)。在app.toml中设置swagger定义了是否应该自动注册swagger文档。


原文链接:https://docs.cosmos.network/main/building-modules/invariants.html

https://docs.cosmos.network/main/building-modules/genesis.html

https://docs.cosmos.network/main/building-modules/module-interfaces.html


---煤油灯科技victorlamp.com翻译整理---


你可能也喜欢这些文章

科技互联网,科技,资讯,动态,洞察,量子,计算,AI,人工智能,机器人,区块链,Web3,分布式,操作系统,OS,芯片,视频,深度,论文