cosmos-starport应用案例

starport 应用案例

存证

编译好starport,开始构建应用案例

构建项目

可以通过运行来构建我们的应用程序starport app github.com/user/pofe。这将创建一个名为的新文件夹pofe(Proof of File Existence)

starport app github.com/user/pofe --sdk-version launchpad

运行程序

搭建好应用程序之后,让我们在新创建的pofe文件夹中打开一个单独的终端窗口,然后运行starport serve,这将启动我们的应用程序

$ starport serve
Cosmos' version is: Launchpad

📦 Installing dependencies...
🛠️  Building the app...
🙂 Created an account. Password (mnemonic): joy manage firm arrange finish bounce power shove bring bundle issue chief quiz spread symptom sword slender vote hobby water weird trim panther slot
🙂 Created an account. Password (mnemonic): meat skirt slab smart impulse region clump genuine zero clog version father spray midnight tip poem input fantasy decide buddy work strategy nest shaft
🌍 Running a Cosmos 'pofe' app with Tendermint at http://localhost:26657.
🌍 Running a server at http://localhost:1317 (LCD)

🚀 Get started: http://localhost:12345/

创建type类型

pofe目录内打开一个新的终端,并创建一个名为claim field的类型proof

starport type claim proof:string

输出:

🎉 Created a type `claim`.

这将创建claim类型,并添加其相关的CLI命令,处理程序,消息,类型,查询器和保持器。

至此,我们有了一个正在运行的应用程序-您可以通过检查辅助终端窗口的输出来验证这一点。

修改应用程序

x/pofe/client/cli/txClaim.go

首先,确保添加以下软件包的导入

package cli

import (
	...

	"crypto/sha256"
	"encoding/hex"
	"io/ioutil"
)

接下来,我们要更新GetCmdCreateClaim函数,使其看起来如下所示:

// CLI transaction command to create a claim
func GetCmdCreateClaim(cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "create-claim [path-to-file]",
		Short: "Creates a new claim from a path to a file",
		Args:  cobra.MinimumNArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			// accept a filepath, read the file, and hash it
			hasher := sha256.New()
			s, _ := ioutil.ReadFile(args[0])
			hasher.Write(s)
			argsProof := hex.EncodeToString(hasher.Sum(nil))

			// automatically scaffolded by `starport type`
			cliCtx := context.NewCLIContext().WithCodec(cdc)
			inBuf := bufio.NewReader(cmd.InOrStdin())
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
			msg := types.NewMsgCreateClaim(cliCtx.GetFromAddress(), string(argsProof))
			err := msg.ValidateBasic()
			if err != nil {
				return err
			}
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
		},
	}
}

我们可以保持GetCmdSetClaimGetCmdDeleteClaim功能不变。

最后,ID我们将使用Proof结构中的值,而不是使用自动生成的uuid作为键。这将使查询Proof数据库中的事件变得容易得多。我们可以通过修改开始CreateClaim在我们的方法./x/pofe/keeper/claim.go文件,以及所有其他相关文件的使用claim.ID,使用claim.Proof替代claim.ID

x/pofe/keeper/claim.go

func (k Keeper) CreateClaim(ctx sdk.Context, claim types.Claim) {
	store := ctx.KVStore(k.storeKey)
	key := []byte(types.ClaimPrefix + claim.Proof)
	value := k.cdc.MustMarshalBinaryLengthPrefixed(claim)
	store.Set(key, value)
}

为了导入types.Kepper声明,我们必须handlerMsgCreateClaim.go在修改如下

x/pofe/handerMsgCreateClaim.go

确保包引用和业务代码如下

package pofe

import (
	sdk "github.com/cosmos/cosmos-sdk/types"

	"github.com/user/pofe/x/pofe/keeper"
	"github.com/user/pofe/x/pofe/types"
)

func handleMsgCreateClaim(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreateClaim) (*sdk.Result, error) {
	var claim = types.Claim{
		Creator: msg.Creator,
		Proof:   msg.Proof,
	}
	k.CreateClaim(ctx, claim)

	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
}

提交文件存在证明声明

运行时starport serve,我们还将应用程序构建为名为的二进制文件pofed,因此我们可以使用此文件来提交声明:

pofecli tx pofe create-claim $(of pofed)--from user1

确认交易后,运行:

pofecli q pofe list-claim

你可以看到输出如下

[
  {
    "creator": "cosmos165hphx98d767c99gtm0n7gevq2q0nwrg75pfkd",
    "proof": "534f056e58115dd106d026e00da22a32f8c776a0cd5b3dd6431598d73b5f623c"
  }
]

如果您要删除或删除声明,则可以通过替换存证证明来简单地运行命令

pofecli tx pofe delete-claim 534f056e58115dd106d026e00da22a32f8c776a0cd5b3dd6431598d73b5f623c --from user1

博客

构建项目

可以通过运行来构建我们的应用程序starport app github.com/example/blog 这将创建一个名为的新文件夹blog

starport app github.com/example/blog

修改应用程序

proto/blog/post.proto

定义proto type类型

syntax = "proto3";
package example.blog.blog;

option go_package = "github.com/example/blog/x/blog/types";

import "gogoproto/gogo.proto";

message Post {
  string creator = 1;
  string id = 2;
  string title = 3; 
  string body = 4; 
}

message MsgCreatePost {
  string creator = 1;
  string title = 2; 
  string body = 3; 
}

上面的代码定义了帖子的三个属性:创建者,标题,正文和ID。我们为每个帖子生成唯一的全局ID,并将它们存储为字符串。

键值存储中的帖子将如下所示

"post-0": {
  "Creator": "cosmos18cd5t4msvp2lpuvh99rwglrmjrrw9qx5h3f3gz",
  "Title": "This is a post!",
  "Body": "Welcome to my blog app.",
  "ID": "0"
},
"post-1": {
  ...
}

x / blog / client / cli / tx.go

在该import块中,确保导入以下四个软件包:

import (
	"fmt"

	"github.com/spf13/cobra"

	"github.com/cosmos/cosmos-sdk/client"
	"github.com/cosmos/cosmos-sdk/client/flags"
	"github.com/cosmos/cosmos-sdk/client/tx"
	// "github.com/cosmos/cosmos-sdk/client/flags"
	"github.com/example/blog/x/blog/types"
)

该文件已包含func GetTxCmd定义自定义blogd 我们将自定义添加create-post命令我们blogd首先加入GetCmdCreatePostblogTxCmd

  cmd.AddCommand(CmdCreatePost())

在文件末尾,让我们定义GetCmdCreatePost

func CmdCreatePost() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "create-post [title] [body]",
		Short: "Creates a new post",
		Args:  cobra.ExactArgs(2),
		RunE: func(cmd *cobra.Command, args []string) error {
      argsTitle := string(args[0])
      argsBody := string(args[1])
      
			clientCtx, err := client.GetClientTxContext(cmd)
			if err != nil {
				return err
			}

			msg := types.NewMsgCreatePost(clientCtx.GetFromAddress().String(), string(argsTitle), string(argsBody))
			if err := msg.ValidateBasic(); err != nil {
				return err
			}
			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
		},
	}

	flags.AddTxFlagsToCmd(cmd)

    return cmd
}

上面的函数定义了运行create-post子命令时发生的情况。create-post接受两个参数[title] [body],创建一条消息NewMsgCreatePost(标题为args[0]args[1]),并广播此消息在应用程序中进行处理。

x / blog / types / messages_post.go

让我们NewMsgCreatePost在一个应创建为的新文件中进行定义x/blog/types/messages_post.go

package types

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var _ sdk.Msg = &MsgCreatePost{}
func NewMsgCreatePost(creator string, title string, body string) *MsgCreatePost {
  return &MsgCreatePost{
		Creator: creator,
    Title: title,
    Body: body,
	}
}

NewMsgCreatePost是创建MsgCreatePost消息的构造函数。必须定义以下五个功能以实现该Msg接口。它们允许您执行不需要访问商店的验证(例如检查空值)等

// Route ...
func (msg MsgCreatePost) Route() string {
  return RouterKey
}
// Type ...
func (msg MsgCreatePost) Type() string {
  return "CreatePost"
}
// GetSigners ...
func (msg *MsgCreatePost) GetSigners() []sdk.AccAddress {
  creator, err := sdk.AccAddressFromBech32(msg.Creator)
  if err != nil {
    panic(err)
  }
  return []sdk.AccAddress{creator}
}
// GetSignBytes ...
func (msg *MsgCreatePost) GetSignBytes() []byte {
  bz := ModuleCdc.MustMarshalJSON(msg)
  return sdk.MustSortJSON(bz)
}
// ValidateBasic ...
func (msg *MsgCreatePost) ValidateBasic() error {
  _, err := sdk.AccAddressFromBech32(msg.Creator)
  	if err != nil {
  		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err)
  	}
  return nil
}

让我们再回到GetCmdCreatePostx/blog/client/cli/tx.go,你会看到MsgCreatePost正在创建和广播用GenerateOrBroadcastMsgs

x / blog / handler.go

func NewHandler定义了列出所有可用处理程序的内容后。对其进行修改以包括一个名为的新函数handleMsgCreatePost

    switch msg := msg.(type) {
    case *types.MsgCreatePost:
        return handleMsgCreatePost(ctx, k, msg)
    default:

x / blog / handler_post.go

现在让我们handleMsgCreatePost在一个新文件中定义handler_post.go

package blog

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/example/blog/x/blog/keeper"
	"github.com/example/blog/x/blog/types"
)

func handleMsgCreatePost(ctx sdk.Context, k keeper.Keeper, msg *types.MsgCreatePost) (*sdk.Result, error) {
	k.CreatePost(ctx, *msg)

	return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil
}

x / blog / keeper / post.go x / blog / keeper / post.go

首先,post.gokeeper/目录中创建一个新文件。然后,添加一个带有CreatePost两个参数的函数,另外,GetPostCount还有SetPostCount functions

package keeper

import (
	"strconv"

	"github.com/cosmos/cosmos-sdk/store/prefix"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/example/blog/x/blog/types"
)

// GetPostCount get the total number of post
func (k Keeper) GetPostCount(ctx sdk.Context) int64 {
	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostCountKey))
	byteKey := types.KeyPrefix(types.PostCountKey)
	bz := store.Get(byteKey)

	// Count doesn't exist: no element
	if bz == nil {
		return 0
	}

	// Parse bytes
	count, err := strconv.ParseInt(string(bz), 10, 64)
	if err != nil {
		// Panic because the count should be always formattable to int64
		panic("cannot decode count")
	}

	return count
}

// SetPostCount set the total number of post
func (k Keeper) SetPostCount(ctx sdk.Context, count int64) {
	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostCountKey))
	byteKey := types.KeyPrefix(types.PostCountKey)
	bz := []byte(strconv.FormatInt(count, 10))
	store.Set(byteKey, bz)
}

func (k Keeper) CreatePost(ctx sdk.Context, msg types.MsgCreatePost) {
	// Create the post
	count := k.GetPostCount(ctx)
	var post = types.Post{
		Creator: msg.Creator,
		Id:      strconv.FormatInt(count, 10),
		Title:   msg.Title,
		Body:    msg.Body,
	}

	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostKey))
	key := types.KeyPrefix(types.PostKey + post.Id)
	value := k.cdc.MustMarshalBinaryBare(&post)
	store.Set(key, value)

	// Update post count
	k.SetPostCount(ctx, count+1)
}

func (k Keeper) GetPost(ctx sdk.Context, key string) types.Post {
	store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostKey))
	var post types.Post
	k.cdc.MustUnmarshalBinaryBare(store.Get(types.KeyPrefix(types.PostKey + key)), &post)
	return post
}

func (k Keeper) HasPost(ctx sdk.Context, id string) bool {
	store :=  prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostKey))
	return store.Has(types.KeyPrefix(types.PostKey + id))
}

func (k Keeper) GetPostOwner(ctx sdk.Context, key string) string {
    return k.GetPost(ctx, key).Creator
}

func (k Keeper) GetAllPost(ctx sdk.Context) (msgs []types.Post) {
    store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.PostKey))
	iterator := sdk.KVStorePrefixIterator(store, types.KeyPrefix(types.PostKey))

	defer iterator.Close()

	for ; iterator.Valid(); iterator.Next() {
		var msg types.Post
		k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &msg)
        msgs = append(msgs, msg)
	}

    return
}


CreatePost通过将发布前缀与ID串联来创建密钥。如果您回顾一下我们商店的外观,您会发现键具有前缀,这就是为什么post-0bae9f7d-20f8-4b51-9d5c-af9103177d66包含prefix的原因post-。这样做的原因是您拥有一个商店,但是您可能想要在其中保留不同类型的对象,例如帖子和用户。带post-和的前缀键user-使您可以在不同类型的对象之间共享一个存储空间

x / blog / types / keys.go

package types

const (
  // Other constants...
  // PostPrefix is used for keys in the KV store
  	PostKey= "Post-value-"
	  PostCountKey= "Post-count-"
)

x/blog/types/codec.go

package types

import (
	"github.com/cosmos/cosmos-sdk/codec"
    cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
    sdk "github.com/cosmos/cosmos-sdk/types"
)

func RegisterCodec(cdc *codec.LegacyAmino) {
    // this line is used by starport scaffolding # 2
  cdc.RegisterConcrete(&MsgCreatePost{}, "blog/CreatePost", nil)

} 

func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
    // this line is used by starport scaffolding # 3
  registry.RegisterImplementations((*sdk.Msg)(nil),
    &MsgCreatePost{},
  )
}

var (
	amino = codec.NewLegacyAmino()
	ModuleCdc = codec.NewAminoCodec(amino)
)

运行

starport serve

此命令将安装依赖项,生成并初始化应用程序,并运行服务器

运行以下命令来创建帖子:

blogd tx blog create-post "My first post" "This is a post\!" --from=user1

“这是一个帖子!” 是我们帖子的标题,并--from=user1告诉程序谁在创建此帖子。user1是用于签名交易的一对密钥的标签

运行命令并进行确认后,您将看到一个具有“ txhash”属性的对象,其值类似于CA1491B39384A4F29E568F62B156E0F2D0601507EF499CE1B8F3930BAFE7F03C

要验证交易是否已处理,请打开浏览器并访问以下URL(确保将其替换CA14...为txhash的值,但确保具有0x前缀)

http://localhost:26657/tx?hash=0xCA1491B39384A4F29E568F62B156E0F2D0601507EF499CE1B8F3930BAFE7F03C

拾荒者狩猎游戏

寻宝游戏是关于某人设置任务或问题的方法,这些问题或挑战会挑战参与者以找到附有某种奖励的解决方案。游戏的基本机制如下:

  • 任何人都可以发布带有加密答案的问题。
  • 这个问题伴随着大量的硬币。
  • 任何人都可以发布该问题的答案,如果他们答对了,他们将获得赏金。

这里要注意的是,在处理具有延迟的公共网络时,可能会发生中间人攻击可能发生。攻击者不会假装成为一方,而会从一方获取敏感信息并将其用于自己的利益。这实际上称为前跑并发生如下情况:

  1. 您发布了某个问题的答案并遭到了赏金攻击。
  2. 有人看到您发布答案,并在您之前将其自己发布。
  3. 由于他们首先发布了答案,因此他们获得的不是您而是您的奖励。

为了防止前端运行,我们将实施一个提交发布计划。提交-披露方案转换单个可利用的交互并将其转换为两个安全的交互。

第一个交互是提交。在这里,您“承诺”在后续互动中发布答案。该提交由您的姓名的加密哈希值和您认为正确的答案组成。该应用程序会保存该值,声称您知道答案,但尚未确认答案是否正确。

下一个交互是显示。您可以在此处以纯文本形式发布答案以及您的姓名。该应用程序将获取您的答案和您的姓名,并对其进行加密哈希处理。如果结果与您先前在提交阶段提交的内容相匹配,则将证明实际上知道答案的是您自己,而不是仅仅领先您的人。
在这里插入图片描述

这样的系统可以不信任的方式与任何类型的游戏平台一起使用。想象一下,您正在玩《塞尔达传说》,而游戏已经编译好,其中包括对不同寻宝游戏的所有答案。当您达到某个级别时,游戏可能会透露出秘密答案。然后,无论是明确地还是在幕后,此答案都可以与您的名字组合在一起,进行哈希处理,提交并随后显示。您的名字将得到奖励,并且您将在游戏中获得更多积分。

实现此目的的另一种方法是拥有一个访问控制列表,其中有一个由视频游戏公司控制的管理员帐户。该管理员帐户可以确认您是否达到了要求,然后给您积分。问题是它会造成单点故障和一个试图攻击系统的目标。如果有一把钥匙统治着城堡,那么如果该钥匙被盗用,那么整个系统就会被破坏。如果该Admin帐户必须一直在线以使玩家获得积分,这也会造成协调问题。如果您使用提交显示系统,那么您将拥有一个更加不受信任的体系结构,在该体系结构中不需要播放权限。这个设计决定有其优点和缺点,但与精心实施相结合,可以使您的游戏得以扩展,而不会出现瓶颈或故障点

构建项目并运行服务

创建名为scavenge的项目

starport app github.com/github-username/scavenge --sdk-version="launchpad"

运行服务

starport serve

创建type类型

在项目文件夹下打开一个新终端,然后运行以下starport type命令来生成我们的scavenge类型

starport type scavenge description solutionHash reward solution scavenger

我们还要创建第二种类型,Commit以防止前面提到的提交的解决方案在前端运行

starport type commit solutionHash solutionScavengerHash

Messages模块代码修改

Messages types位于./x/scavenge/types/目录内。您可以看到该type命令已经设置了MsgCreateScavenge.go文件。

在这个新文件中,我们将删除创建清理时不会使用的某些字段

package types

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var _ sdk.Msg = &MsgCreateScavenge{}

type MsgCreateScavenge struct {
	Creator      sdk.AccAddress `json:"creator" yaml:"creator"`
	Description  string         `json:"description" yaml:"description"`
	SolutionHash string         `json:"solutionHash" yaml:"solutionHash"`
	Reward       sdk.Coins      `json:"reward" yaml:"reward"`
}

func NewMsgCreateScavenge(creator sdk.AccAddress, description string, solutionHash string, reward sdk.Coins) MsgCreateScavenge {
	return MsgCreateScavenge{
		Creator:      creator,
		Description:  description,
		SolutionHash: solutionHash,
		Reward:       reward,
	}
}

func (msg MsgCreateScavenge) Route() string {
	return RouterKey
}

func (msg MsgCreateScavenge) Type() string {
	return "CreateScavenge"
}

func (msg MsgCreateScavenge) GetSigners() []sdk.AccAddress {
	return []sdk.AccAddress{sdk.AccAddress(msg.Creator)}
}

func (msg MsgCreateScavenge) GetSignBytes() []byte {
	bz := ModuleCdc.MustMarshalJSON(msg)
	return sdk.MustSortJSON(bz)
}

func (msg MsgCreateScavenge) ValidateBasic() error {
	if msg.Creator.Empty() {
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "creator can't be empty")
	}
	if msg.SolutionHash == "" {
		return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "solutionHash can't be empty")
	}
	return nil
}

应用程序中的所有消息都需要遵循该sdk.Msg界面。消息struct包含创建新清除时的所有必要信息:

  • Creator-谁创造的。这使用sdk.AccAddress代表由公共密钥cryptograhy控制的应用程序中的帐户的类型。
  • Description -要解决的问题或对挑战的描述。
  • SolutionHash -混乱的解决方案。
  • Reward -这是奖励给谁先提交答案的人。

Msg界面还需要设置其他方法,例如,验证的内容struct以及确认消息是由创建者签名并提交的。

既然可以创建清除方法,那么唯一的其他基本操作就是能够解决它。如前所述,这应分为两个单独的操作:MsgCommitSolutionMsgRevealSolution

将我们重命名./x/scavenge/types/MsgCreateCommit.go./x/scavenge/types/MsgCommitSolution.go

替换函数参数以反映MsgCommitSolution而不是以前的MsgCreateCommit

描述如何提交解决方案的消息类型应该存在./x/scavenge/types/MsgCommitSolution.go,如下所示

package types

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var _ sdk.Msg = &MsgCommitSolution{}

type MsgCommitSolution struct {
	Scavenger             sdk.AccAddress `json:"scavenger" yaml:"scavenger"`                         // address of the scavenger
	SolutionHash          string         `json:"solutionhash" yaml:"solutionhash"`                   // solutionhash of the scavenge
	SolutionScavengerHash string         `json:"solutionScavengerHash" yaml:"solutionScavengerHash"` // solution hash of the scavenge
}

// NewMsgCommitSolution creates a new MsgCommitSolution instance
func NewMsgCommitSolution(scavenger sdk.AccAddress, solutionHash string, solutionScavengerHash string) MsgCommitSolution {
	return MsgCommitSolution{
		Scavenger:             scavenger,
		SolutionHash:          solutionHash,
		SolutionScavengerHash: solutionScavengerHash,
	}
}

func (msg MsgCommitSolution) Route() string {
	return RouterKey
}

func (msg MsgCommitSolution) Type() string {
	return "CreateCommit"
}

func (msg MsgCommitSolution) GetSigners() []sdk.AccAddress {
	return []sdk.AccAddress{sdk.AccAddress(msg.Scavenger)}
}

func (msg MsgCommitSolution) GetSignBytes() []byte {
	bz := ModuleCdc.MustMarshalJSON(msg)
	return sdk.MustSortJSON(bz)
}

func (msg MsgCommitSolution) ValidateBasic() error {
	if msg.Scavenger.Empty() {
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "scavenger can't be empty")
	}
	return nil
}

消息struct包含揭示解决方案时的所有必要信息:

  • Scavenger -谁在透露解决方案。
  • SolutionHash -混乱的解决方案(哈希)。
  • SolutionScavengerHash -这是解决方案和解决方案的人的哈希组合。

该消息也实现了sdk.Msg接口。

此消息类型应该存在./x/scavenge/types/MsgRevealSolution.go

package types

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// MsgRevealSolution
// ------------------------------------------------------------------------------
var _ sdk.Msg = &MsgRevealSolution{}

// MsgRevealSolution - struct for unjailing jailed validator
type MsgRevealSolution struct {
	Scavenger    sdk.AccAddress `json:"scavenger" yaml:"scavenger"`       // address of the scavenger scavenger
	SolutionHash string         `json:"solutionHash" yaml:"solutionHash"` // SolutionHash of the scavenge
	Solution     string         `json:"solution" yaml:"solution"`         // solution of the scavenge
}

// NewMsgRevealSolution creates a new MsgRevealSolution instance
func NewMsgRevealSolution(scavenger sdk.AccAddress, solution string) MsgRevealSolution {

	var solutionHash = sha256.Sum256([]byte(solution))
	var solutionHashString = hex.EncodeToString(solutionHash[:])

	return MsgRevealSolution{
		Scavenger:    scavenger,
		SolutionHash: solutionHashString,
		Solution:     solution,
	}
}

// RevealSolutionConst is RevealSolution Constant
const RevealSolutionConst = "RevealSolution"

// nolint
func (msg MsgRevealSolution) Route() string { return RouterKey }
func (msg MsgRevealSolution) Type() string  { return RevealSolutionConst }
func (msg MsgRevealSolution) GetSigners() []sdk.AccAddress {
	return []sdk.AccAddress{sdk.AccAddress(msg.Scavenger)}
}

// GetSignBytes gets the bytes for the message signer to sign on
func (msg MsgRevealSolution) GetSignBytes() []byte {
	bz := ModuleCdc.MustMarshalJSON(msg)
	return sdk.MustSortJSON(bz)
}

// ValidateBasic validity check for the AnteHandler
func (msg MsgRevealSolution) ValidateBasic() error {
	if msg.Scavenger.Empty() {
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "scavenger can't be empty")
	}
	if msg.SolutionHash == "" {
		return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "solutionScavengerHash can't be empty")
	}
	if msg.Solution == "" {
		return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "solutionHash can't be empty")
	}

	var solutionHash = sha256.Sum256([]byte(msg.Solution))
	var solutionHashString = hex.EncodeToString(solutionHash[:])

	if msg.SolutionHash != solutionHashString {
		return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("Hash of solution (%s) doesn't equal solutionHash (%s)", msg.SolutionHash, solutionHashString))
	}
	return nil
}

消息struct包含揭示解决方案时的所有必要信息:

  • Scavenger -谁在透露解决方案。
  • SolutionHash -混乱的解决方案。
  • Solution -解决方案的纯文本版本。

该消息也实现了sdk.Msg接口。

特别是看ValidateBasic功能。它验证是否进行了所有必要的输入以显示解决方案,并从提交的解决方案中创建了sha256哈希。

MsgSetScavenge

package types

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var _ sdk.Msg = &MsgSetScavenge{}

type MsgSetScavenge struct {
	Creator      sdk.AccAddress `json:"creator" yaml:"creator"`
	Description  string         `json:"description" yaml:"description"`
	SolutionHash string         `json:"solutionHash" yaml:"solutionHash"`
	Reward       string         `json:"reward" yaml:"reward"`
	Solution     string         `json:"solution" yaml:"solution"`
	Scavenger    sdk.AccAddress `json:"scavenger" yaml:"scavenger"`
}

func NewMsgSetScavenge(creator sdk.AccAddress, description string, solutionHash string, reward string, solution string, scavenger sdk.AccAddress) MsgSetScavenge {
	return MsgSetScavenge{
		Creator:      creator,
		Description:  description,
		SolutionHash: solutionHash,
		Reward:       reward,
		Solution:     solution,
		Scavenger:    scavenger,
	}
}

func (msg MsgSetScavenge) Route() string {
	return RouterKey
}

func (msg MsgSetScavenge) Type() string {
	return "SetScavenge"
}

func (msg MsgSetScavenge) GetSigners() []sdk.AccAddress {
	return []sdk.AccAddress{sdk.AccAddress(msg.Creator)}
}

func (msg MsgSetScavenge) GetSignBytes() []byte {
	bz := ModuleCdc.MustMarshalJSON(msg)
	return sdk.MustSortJSON(bz)
}

func (msg MsgSetScavenge) ValidateBasic() error {
	if msg.Creator.Empty() {
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "creator can't be empty")
	}
	return nil
}

MsgDeleteScavenge

package types

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var _ sdk.Msg = &MsgDeleteScavenge{}

type MsgDeleteScavenge struct {
  ID      string         `json:"id" yaml:"id"`
  Creator sdk.AccAddress `json:"creator" yaml:"creator"`
}

func NewMsgDeleteScavenge(id string, creator sdk.AccAddress) MsgDeleteScavenge {
  return MsgDeleteScavenge{
    ID: id,
		Creator: creator,
	}
}

func (msg MsgDeleteScavenge) Route() string {
  return RouterKey
}

func (msg MsgDeleteScavenge) Type() string {
  return "DeleteScavenge"
}

func (msg MsgDeleteScavenge) GetSigners() []sdk.AccAddress {
  return []sdk.AccAddress{sdk.AccAddress(msg.Creator)}
}

func (msg MsgDeleteScavenge) GetSignBytes() []byte {
  bz := ModuleCdc.MustMarshalJSON(msg)
  return sdk.MustSortJSON(bz)
}

func (msg MsgDeleteScavenge) ValidateBasic() error {
  if msg.Creator.Empty() {
    return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "creator can't be empty")
  }
  return nil
}

MsgSetCommit

package types

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var _ sdk.Msg = &MsgSetCommit{}

type MsgSetCommit struct {
	Scavenger             sdk.AccAddress `json:"scavenger" yaml:"scavenger"`
	SolutionHash          string         `json:"solutionHash" yaml:"solutionHash"`
	SolutionScavengerHash string         `json:"solutionScavengerHash" yaml:"solutionScavengerHash"`
}

func NewMsgSetCommit(scavenger sdk.AccAddress, id string, solutionHash string, solutionScavengerHash string) MsgSetCommit {
	return MsgSetCommit{
		Scavenger:             scavenger,
		SolutionHash:          solutionHash,
		SolutionScavengerHash: solutionScavengerHash,
	}
}

func (msg MsgSetCommit) Route() string {
	return RouterKey
}

func (msg MsgSetCommit) Type() string {
	return "SetCommit"
}

func (msg MsgSetCommit) GetSigners() []sdk.AccAddress {
	return []sdk.AccAddress{sdk.AccAddress(msg.Scavenger)}
}

func (msg MsgSetCommit) GetSignBytes() []byte {
	bz := ModuleCdc.MustMarshalJSON(msg)
	return sdk.MustSortJSON(bz)
}

func (msg MsgSetCommit) ValidateBasic() error {
	if msg.Scavenger.Empty() {
		return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "scavenger can't be empty")
	}
	return nil
}

MsgDeleteCommit

package types

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var _ sdk.Msg = &MsgDeleteCommit{}

type MsgDeleteCommit struct {
  ID      string         `json:"id" yaml:"id"`
  Creator sdk.AccAddress `json:"creator" yaml:"creator"`
}

func NewMsgDeleteCommit(id string, creator sdk.AccAddress) MsgDeleteCommit {
  return MsgDeleteCommit{
    ID: id,
		Creator: creator,
	}
}

func (msg MsgDeleteCommit) Route() string {
  return RouterKey
}

func (msg MsgDeleteCommit) Type() string {
  return "DeleteCommit"
}

func (msg MsgDeleteCommit) GetSigners() []sdk.AccAddress {
  return []sdk.AccAddress{sdk.AccAddress(msg.Creator)}
}

func (msg MsgDeleteCommit) GetSignBytes() []byte {
  bz := ModuleCdc.MustMarshalJSON(msg)
  return sdk.MustSortJSON(bz)
}

func (msg MsgDeleteCommit) ValidateBasic() error {
  if msg.Creator.Empty() {
    return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "creator can't be empty")
  }
  return nil
}

编解码器

定义消息后,我们需要向编码器描述如何将其存储为字节。为此,我们编辑位于的文件./x/scavenge/types/codec.go。通过如下描述我们的类型,它们将与我们的编码库一起使用

package types

import (
	"github.com/cosmos/cosmos-sdk/codec"
)

// RegisterCodec registers concrete types on codec
func RegisterCodec(cdc *codec.Codec) {
	// this line is used by starport scaffolding # 1
	cdc.RegisterConcrete(MsgCommitSolution{}, "scavenge/CreateCommit", nil)
	cdc.RegisterConcrete(MsgSetCommit{}, "scavenge/SetCommit", nil)
	cdc.RegisterConcrete(MsgDeleteCommit{}, "scavenge/DeleteCommit", nil)
	cdc.RegisterConcrete(MsgCreateScavenge{}, "scavenge/CreateScavenge", nil)
	cdc.RegisterConcrete(MsgSetScavenge{}, "scavenge/SetScavenge", nil)
	cdc.RegisterConcrete(MsgDeleteScavenge{}, "scavenge/DeleteScavenge", nil)
	cdc.RegisterConcrete(MsgRevealSolution{}, "scavenge/MsgRevealSolution", nil)
}

// ModuleCdc defines the module codec
var ModuleCdc *codec.Codec

func init() {
	ModuleCdc = codec.New()
	RegisterCodec(ModuleCdc)
	codec.RegisterCrypto(ModuleCdc)
	ModuleCdc.Seal()
}

Keep模块代码修改

使用该starport命令后,您应该Keeper在处有一个样板./x/scavenge/keeper/keeper.go。它包含了像基本功能引用一个基本的门将SetGetDelete

管理员将所有数据存储在模块中。有时一个模块会导入另一个模块的管理器。这将允许在模块之间共享和修改状态。由于我们在处理模块中的coin作为赏金奖励,因此我们需要访问bank模块的管理员(我们称之为CoinKeeper)。看看我们完成的Keeper文件,你可以看到那里的bank门将被引用,以及如何SetGet以及Delete展开

keeper/keeper.go
package keeper

import (
	"fmt"

	"github.com/tendermint/tendermint/libs/log"

	"github.com/cosmos/cosmos-sdk/codec"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/cosmos/cosmos-sdk/x/bank"
	"github.com/github-username/scavenge/x/scavenge/types"
)

// Keeper of the scavenge store
type Keeper struct {
	CoinKeeper bank.Keeper
	storeKey   sdk.StoreKey
	cdc        *codec.Codec
	// paramspace types.ParamSubspace
}

// NewKeeper creates a scavenge keeper
func NewKeeper(coinKeeper bank.Keeper, cdc *codec.Codec, key sdk.StoreKey) Keeper {
	keeper := Keeper{
		CoinKeeper: coinKeeper,
		storeKey:   key,
		cdc:        cdc,
		// paramspace: paramspace.WithKeyTable(types.ParamKeyTable()),
	}
	return keeper
}

// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
	return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}

// Get returns the pubkey from the adddress-pubkey relation
// func (k Keeper) Get(ctx sdk.Context, key string) (/* TODO: Fill out this type */, error) {
// 	store := ctx.KVStore(k.storeKey)
// 	var item /* TODO: Fill out this type */
// 	byteKey := []byte(key)
// 	err := k.cdc.UnmarshalBinaryLengthPrefixed(store.Get(byteKey), &item)
// 	if err != nil {
// 		return nil, err
// 	}
// 	return item, nil
// }

// func (k Keeper) set(ctx sdk.Context, key string, value /* TODO: fill out this type */ ) {
// 	store := ctx.KVStore(k.storeKey)
// 	bz := k.cdc.MustMarshalBinaryLengthPrefixed(value)
// 	store.Set([]byte(key), bz)
// }

// func (k Keeper) delete(ctx sdk.Context, key string) {
// 	store := ctx.KVStore(k.storeKey)
// 	store.Delete([]byte(key))
// }
keeper/scavenge.go
package keeper

import (
	"strconv"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

	"github.com/cosmos/cosmos-sdk/codec"
	"github.com/github-username/scavenge/x/scavenge/types"
)

// GetScavengeCount get the total number of scavenge
func (k Keeper) GetScavengeCount(ctx sdk.Context) int64 {
	store := ctx.KVStore(k.storeKey)
	byteKey := []byte(types.ScavengeCountPrefix)
	bz := store.Get(byteKey)

	// Count doesn't exist: no element
	if bz == nil {
		return 0
	}

	// Parse bytes
	count, err := strconv.ParseInt(string(bz), 10, 64)
	if err != nil {
		// Panic because the count should be always formattable to int64
		panic("cannot decode count")
	}

	return count
}

// SetScavengeCount set the total number of scavenge
func (k Keeper) SetScavengeCount(ctx sdk.Context, count int64) {
	store := ctx.KVStore(k.storeKey)
	byteKey := []byte(types.ScavengeCountPrefix)
	bz := []byte(strconv.FormatInt(count, 10))
	store.Set(byteKey, bz)
}

// CreateScavenge creates a scavenge
func (k Keeper) CreateScavenge(ctx sdk.Context, scavenge types.Scavenge) {
	store := ctx.KVStore(k.storeKey)
	key := []byte(types.ScavengePrefix + scavenge.SolutionHash)
	value := k.cdc.MustMarshalBinaryLengthPrefixed(scavenge)
	store.Set(key, value)
}

// GetScavenge returns the scavenge information
func (k Keeper) GetScavenge(ctx sdk.Context, solutionHash string) (types.Scavenge, error) {
	store := ctx.KVStore(k.storeKey)
	var scavenge types.Scavenge
	byteKey := []byte(types.ScavengePrefix + solutionHash)
	err := k.cdc.UnmarshalBinaryLengthPrefixed(store.Get(byteKey), &scavenge)
	if err != nil {
		return scavenge, err
	}
	return scavenge, nil
}

// SetScavenge sets a scavenge
func (k Keeper) SetScavenge(ctx sdk.Context, scavenge types.Scavenge) {
	solutionHash := scavenge.SolutionHash
	store := ctx.KVStore(k.storeKey)
	bz := k.cdc.MustMarshalBinaryLengthPrefixed(scavenge)
	key := []byte(types.ScavengePrefix + solutionHash)

	// You can perform additional checks here, depending on your use case

	store.Set(key, bz)
}

// DeleteScavenge deletes a scavenge
func (k Keeper) DeleteScavenge(ctx sdk.Context, solutionHash string) {
	store := ctx.KVStore(k.storeKey)
	store.Delete([]byte(types.ScavengePrefix + solutionHash))
}

//
// Functions used by querier
//

func listScavenge(ctx sdk.Context, k Keeper) ([]byte, error) {
	var scavengeList []types.Scavenge
	store := ctx.KVStore(k.storeKey)
	iterator := sdk.KVStorePrefixIterator(store, []byte(types.ScavengePrefix))
	for ; iterator.Valid(); iterator.Next() {
		var scavenge types.Scavenge
		k.cdc.MustUnmarshalBinaryLengthPrefixed(store.Get(iterator.Key()), &scavenge)
		scavengeList = append(scavengeList, scavenge)
	}
	res := codec.MustMarshalJSONIndent(k.cdc, scavengeList)
	return res, nil
}

func getScavenge(ctx sdk.Context, path []string, k Keeper) (res []byte, sdkError error) {
	solutionHash := path[0]
	scavenge, err := k.GetScavenge(ctx, solutionHash)
	if err != nil {
		return nil, err
	}

	res, err = codec.MarshalJSONIndent(k.cdc, scavenge)
	if err != nil {
		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
	}

	return res, nil
}

// Get creator of the item
func (k Keeper) GetScavengeOwner(ctx sdk.Context, key string) sdk.AccAddress {
	scavenge, err := k.GetScavenge(ctx, key)
	if err != nil {
		return nil
	}
	return scavenge.Creator
}

// Check if the key exists in the store
func (k Keeper) ScavengeExists(ctx sdk.Context, key string) bool {
	store := ctx.KVStore(k.storeKey)
	return store.Has([]byte(types.ScavengePrefix + key))
}

keeper/commit.go
package keeper

import (
	"strconv"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

	"github.com/cosmos/cosmos-sdk/codec"
	"github.com/github-username/scavenge/x/scavenge/types"
)

// GetCommitCount get the total number of commit
func (k Keeper) GetCommitCount(ctx sdk.Context) int64 {
	store := ctx.KVStore(k.storeKey)
	byteKey := []byte(types.CommitCountPrefix)
	bz := store.Get(byteKey)

	// Count doesn't exist: no element
	if bz == nil {
		return 0
	}

	// Parse bytes
	count, err := strconv.ParseInt(string(bz), 10, 64)
	if err != nil {
		// Panic because the count should be always formattable to int64
		panic("cannot decode count")
	}

	return count
}

// SetCommitCount set the total number of commit
func (k Keeper) SetCommitCount(ctx sdk.Context, count int64) {
	store := ctx.KVStore(k.storeKey)
	byteKey := []byte(types.CommitCountPrefix)
	bz := []byte(strconv.FormatInt(count, 10))
	store.Set(byteKey, bz)
}

// CreateCommit creates a commit
func (k Keeper) CreateCommit(ctx sdk.Context, commit types.Commit) {
	store := ctx.KVStore(k.storeKey)
	key := []byte(types.CommitPrefix + commit.SolutionScavengerHash)
	value := k.cdc.MustMarshalBinaryLengthPrefixed(commit)
	store.Set(key, value)
}

func (k Keeper) GetCommit(ctx sdk.Context, key string) (types.Commit, error) {
	store := ctx.KVStore(k.storeKey)
	var commit types.Commit
	byteKey := []byte(types.CommitPrefix + key)
	err := k.cdc.UnmarshalBinaryLengthPrefixed(store.Get(byteKey), &commit)
	if err != nil {
		return commit, err
	}
	return commit, nil
}

// SetCommit sets a commit
func (k Keeper) SetCommit(ctx sdk.Context, commit types.Commit) {
	commitKey := commit.SolutionHash
	store := ctx.KVStore(k.storeKey)
	bz := k.cdc.MustMarshalBinaryLengthPrefixed(commit)
	key := []byte(types.CommitPrefix + commitKey)
	store.Set(key, bz)
}

// DeleteCommit deletes a commit
func (k Keeper) DeleteCommit(ctx sdk.Context, key string) {
	store := ctx.KVStore(k.storeKey)
	store.Delete([]byte(types.CommitPrefix + key))
}

//
// Functions used by querier
//

func getCommit(ctx sdk.Context, path []string, k Keeper) (res []byte, sdkError error) {
	key := path[0]
	commit, err := k.GetCommit(ctx, key)
	if err != nil {
		return nil, err
	}

	res, err = codec.MarshalJSONIndent(k.cdc, commit)
	if err != nil {
		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
	}

	return res, nil
}

func listCommit(ctx sdk.Context, k Keeper) ([]byte, error) {
	var commitList []types.Commit
	store := ctx.KVStore(k.storeKey)
	iterator := sdk.KVStorePrefixIterator(store, []byte(types.CommitPrefix))
	for ; iterator.Valid(); iterator.Next() {
		var commit types.Commit
		k.cdc.MustUnmarshalBinaryLengthPrefixed(store.Get(iterator.Key()), &commit)
		commitList = append(commitList, commit)
	}
	res := codec.MustMarshalJSONIndent(k.cdc, commitList)
	return res, nil
}

// Get creator of the item
func (k Keeper) GetCommitOwner(ctx sdk.Context, key string) sdk.AccAddress {
	commit, err := k.GetCommit(ctx, key)
	if err != nil {
		return nil
	}
	return commit.Scavenger
}

// Check if the key exists in the store
func (k Keeper) CommitExists(ctx sdk.Context, key string) bool {
	store := ctx.KVStore(k.storeKey)
	return store.Has([]byte(types.CommitPrefix + key))
}
Commits and Scavenges

您可能会注意到types.Committypes.Scavenge贯穿了整个参考Keeper。这些是定义的新结构,./x/scavenge/types/type<Type>.go其中包含有关不同清除挑战和针对这些挑战的不同已提交解决方案的所有必要信息。它们看起来类似于Msg我们之前看到的类型,因为它们包含相似的信息。我们将对支架文件进行一些修改。

TypeScavenge.go文件中,我们需要删除该ID字段,因为我们将使用SolutionHash键作为键。我们还需要更新Rewardsdk.Coins,以及Scavengersdk.AccAddress,所以我们可以支付一次扫解决。

完成此操作后,您的结构scavenge/x/scavenge/types/TypeScavenge.go应如下

package types

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
)

type Scavenge struct {
	Creator      sdk.AccAddress `json:"creator" yaml:"creator"`
	Description  string         `json:"description" yaml:"description"`
	SolutionHash string         `json:"solutionHash" yaml:"solutionHash"`
	Reward       sdk.Coins      `json:"reward" yaml:"reward"`
	Solution     string         `json:"solution" yaml:"solution"`
	Scavenger    sdk.AccAddress `json:"scavenger" yaml:"scavenger"`
}

为此TypeCommit.go,我们需要删除该ID字段,然后将该Creator字段重命名为Scavenger

package types

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
)

type Commit struct {
	Scavenger             sdk.AccAddress `json:"scavenger" yaml:"scavenger"`
	SolutionHash          string         `json:"solutionHash" yaml:"solutionHash"`
	SolutionScavengerHash string         `json:"solutionScavengerHash" yaml:"solutionScavengerHash"`
}

您可以想象,未解决的字段Scavenge将包含nil字段的值,Solution然后Scavenger再求解。您可能还注意到每种类型都有该String方法。这有助于将结构呈现为字符串

Prefixes

您可能会注意到的使用types.ScavengePrefixtypes.ScavengeCountPrefix以及types.CommitPrefixtypes.CommitCountPrefix。这些定义在一个名为的文件中./x/scavenge/types/key.go,可帮助我们保持Keeper组织良好。该Keeper实际上只是一个键值存储。这意味着,与Objectjavascript中的相似,所有值都在键下引用。要访问值,您需要知道存储它的键。这有点像唯一标识符(UID)。

在存储a时,Scavenge我们使用的密钥SolutionHash作为唯一ID,对于a时,Commit我们使用的密钥SolutionScavengeHash。但是,由于我们将这两种数据类型存储在同一位置,因此我们可能希望区分用作键的哈希类型。我们可以通过在散列上添加前缀来做到这一点,从而使我们能够识别出哪一个。因为Scavenge我们看到了前缀scavenge-valuescavenge-count,所以Commit我们看到了前缀commit-valuecommit-count

您应该在key.go文件中看到这些内容,因此如下所示

package types

const (
	// ModuleName is the name of the module
	ModuleName = "scavenge"

	// StoreKey to be used when creating the KVStore
	StoreKey = ModuleName

	// RouterKey to be used for routing msgs
	RouterKey = ModuleName

	// QuerierRoute to be used for querier msgs
	QuerierRoute = ModuleName
)

const (
	ScavengePrefix = "scavenge-value-"
	ScavengeCountPrefix = "scavenge-count-"
)
		
const (
	CommitPrefix = "commit-value-"
	CommitCountPrefix = "commit-count-"
)

迭代器

有时,您可能想直接通过其键访问aCommit或a Scavenge。这就是为什么我们有方法GetCommit和的原因GetScavenge

但是,有时您会想要Scavenge一次或一次获取所有内容Commit。为此,我们使用称为的迭代器KVStorePrefixIterator。此实用程序来自,cosmos sdk并在密钥存储上进行迭代。如果提供前缀,它将仅对包含该前缀的键进行迭代。由于我们为Scavenge和我们定义了前缀,因此Commit我们可以在此处使用它们以仅返回所需的数据类型。

Handler模块代码修改

为了使消息到达Keeper,它必须经过Handler。在这里可以应用逻辑来允许或拒绝aMessage成功。这也是逻辑准确显示状态更改应如何在Keeper中进行的地方。如果您熟悉Model View Controller(MVC)架构,Keeper有点像ModelHandler有点像Controller。如果您熟悉React / Redux 或Vue / Vuex架构,Keeper有点像Reducer / Store,而Handler有点像Actions

我们的处理程序将进入./x/scavenge/handler.go并遵循样板中列出的建议。我们将创建一个名为单独的文件处理功能,handler<Action.go为我们的每一个三种Message类型MsgCreateScavengeMsgCommitSolutionMsgRevealSolution

运行starport type命令应该已经添加了handlerMsgCreateScavenge.gohandlerMsgCreateCommit.go文件。本质上,您可以重命名handlerMsgCreateCommithandlerMsgCommitSolution。制作一份副本并将其用作的模板handlerMsgRevealSolution。我们将修改文件如下所示

handlerMsgCreateScavenge.go
package scavenge

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	"github.com/tendermint/tendermint/crypto"

	"github.com/github-username/scavenge/x/scavenge/keeper"
	"github.com/github-username/scavenge/x/scavenge/types"
)

func handleMsgCreateScavenge(ctx sdk.Context, k keeper.Keeper, msg types.MsgCreateScavenge) (*sdk.Result, error) {
	var scavenge = types.Scavenge{
		Creator:      msg.Creator,
		Description:  msg.Description,
		SolutionHash: msg.SolutionHash,
		Reward:       msg.Reward,
	}

	// Ensure no scavenge exists
	_, err := k.GetScavenge(ctx, scavenge.SolutionHash)
	if err == nil {
		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Scavenge with that solution hash already exists")
	}
	moduleAcct := sdk.AccAddress(crypto.AddressHash([]byte(types.ModuleName)))
	sdkError := k.CoinKeeper.SendCoins(ctx, scavenge.Creator, moduleAcct, scavenge.Reward)
	if sdkError != nil {
		return nil, sdkError
	}

	k.CreateScavenge(ctx, scavenge)
	ctx.EventManager().EmitEvent(
		sdk.NewEvent(
			sdk.EventTypeMessage,
			sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
			sdk.NewAttribute(sdk.AttributeKeyAction, types.EventTypeCreateScavenge),
			sdk.NewAttribute(sdk.AttributeKeySender, msg.Creator.String()),
			sdk.NewAttribute(types.AttributeDescription, msg.Description),
			sdk.NewAttribute(types.AttributeSolutionHash, msg.SolutionHash),
			sdk.NewAttribute(types.AttributeReward, msg.Reward.String()),
		),
	)
	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
}

handlerMsgSetScavenge.go
package scavenge

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

	"github.com/github-username/scavenge/x/scavenge/keeper"
	"github.com/github-username/scavenge/x/scavenge/types"
)

func handleMsgSetScavenge(ctx sdk.Context, k keeper.Keeper, msg types.MsgSetScavenge) (*sdk.Result, error) {
	var scavenge = types.Scavenge{
		Creator:      msg.Creator,
		Description:  msg.Description,
		SolutionHash: msg.SolutionHash,
		Solution:     msg.Solution,
	}
	if !msg.Creator.Equals(k.GetScavengeOwner(ctx, msg.SolutionHash)) { // Checks if the the msg sender is the same as the current owner
		return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Incorrect Owner") // If not, throw an error
	}

	k.SetScavenge(ctx, scavenge)

	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
}

handlerMsgDeleteScavenge.go
package scavenge

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

	"github.com/github-username/scavenge/x/scavenge/types"
	"github.com/github-username/scavenge/x/scavenge/keeper"
)

// Handle a message to delete name
func handleMsgDeleteScavenge(ctx sdk.Context, k keeper.Keeper, msg types.MsgDeleteScavenge) (*sdk.Result, error) {
	if !k.ScavengeExists(ctx, msg.ID) {
		// replace with ErrKeyNotFound for 0.39+
		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, msg.ID)
	}
	if !msg.Creator.Equals(k.GetScavengeOwner(ctx, msg.ID)) {
		return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Incorrect Owner")
	}

	k.DeleteScavenge(ctx, msg.ID)
	return &sdk.Result{}, nil
}

handlerMsgCommitSolution.go

重命名handlerMsgCreateCommit.gohandlerMsgCommitSolution.go

package scavenge

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

	"github.com/github-username/scavenge/x/scavenge/keeper"
	"github.com/github-username/scavenge/x/scavenge/types"
)

func handleMsgCommitSolution(ctx sdk.Context, k keeper.Keeper, msg types.MsgCommitSolution) (*sdk.Result, error) {
	var commit = types.Commit{
		Scavenger:             msg.Scavenger,
		SolutionHash:          msg.SolutionHash,
		SolutionScavengerHash: msg.SolutionScavengerHash,
	}
	_, err := k.GetCommit(ctx, commit.SolutionScavengerHash)
	// should produce an error when commit is not found
	if err == nil {
		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Commit with that hash already exists")
	}
	k.CreateCommit(ctx, commit)
	ctx.EventManager().EmitEvent(
		sdk.NewEvent(
			sdk.EventTypeMessage,
			sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
			sdk.NewAttribute(sdk.AttributeKeyAction, types.EventTypeCommitSolution),
			sdk.NewAttribute(sdk.AttributeKeySender, msg.Scavenger.String()),
			sdk.NewAttribute(types.AttributeSolutionHash, msg.SolutionHash),
			sdk.NewAttribute(types.AttributeSolutionScavengerHash, msg.SolutionScavengerHash),
		),
	)
	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
}

handlerMsgSetCommit.go
package scavenge

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

	"github.com/github-username/scavenge/x/scavenge/keeper"
	"github.com/github-username/scavenge/x/scavenge/types"
)

func handleMsgSetCommit(ctx sdk.Context, k keeper.Keeper, msg types.MsgSetCommit) (*sdk.Result, error) {
	var commit = types.Commit{
		Scavenger:             msg.Scavenger,
		SolutionHash:          msg.SolutionHash,
		SolutionScavengerHash: msg.SolutionScavengerHash,
	}
	if !msg.Scavenger.Equals(k.GetCommitOwner(ctx, msg.SolutionHash)) { // Checks if the the msg sender is the same as the current owner
		return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Incorrect Owner") // If not, throw an error
	}

	k.SetCommit(ctx, commit)

	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
}
handlerMsgDeleteCommit.go
package scavenge

import (
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

	"github.com/github-username/scavenge/x/scavenge/types"
	"github.com/github-username/scavenge/x/scavenge/keeper"
)

// Handle a message to delete name
func handleMsgDeleteCommit(ctx sdk.Context, k keeper.Keeper, msg types.MsgDeleteCommit) (*sdk.Result, error) {
	if !k.CommitExists(ctx, msg.ID) {
		// replace with ErrKeyNotFound for 0.39+
		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, msg.ID)
	}
	if !msg.Creator.Equals(k.GetCommitOwner(ctx, msg.ID)) {
		return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Incorrect Owner")
	}

	k.DeleteCommit(ctx, msg.ID)
	return &sdk.Result{}, nil
}
handlerMsgRevealSolution.go
package scavenge

import (
	"crypto/sha256"
	"encoding/hex"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	"github.com/tendermint/tendermint/crypto"

	"github.com/github-username/scavenge/x/scavenge/keeper"
	"github.com/github-username/scavenge/x/scavenge/types"
)

func handleMsgRevealSolution(ctx sdk.Context, k keeper.Keeper, msg types.MsgRevealSolution) (*sdk.Result, error) {
	var solutionScavengerBytes = []byte(msg.Solution + msg.Scavenger.String())
	var solutionScavengerHash = sha256.Sum256(solutionScavengerBytes)
	var solutionScavengerHashString = hex.EncodeToString(solutionScavengerHash[:])
	_, err := k.GetCommit(ctx, solutionScavengerHashString)
	if err != nil {
		return nil, sdkerrors.Wrap(err, "Commit with that hash doesn't exists")
	}

	var solutionHash = sha256.Sum256([]byte(msg.Solution))
	var solutionHashString = hex.EncodeToString(solutionHash[:])
	var scavenge types.Scavenge
	scavenge, err = k.GetScavenge(ctx, solutionHashString)
	if err != nil {
		return nil, sdkerrors.Wrap(err, "Scavenge with that solution hash doesn't exists")
	}
	if scavenge.Scavenger != nil {
		return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Scavenge has already been solved")
	}
	scavenge.Scavenger = msg.Scavenger
	scavenge.Solution = msg.Solution

	moduleAcct := sdk.AccAddress(crypto.AddressHash([]byte(types.ModuleName)))
	sdkError := k.CoinKeeper.SendCoins(ctx, moduleAcct, scavenge.Scavenger, scavenge.Reward)
	if sdkError != nil {
		return nil, sdkError
	}
	k.SetScavenge(ctx, scavenge)
	ctx.EventManager().EmitEvent(
		sdk.NewEvent(
			sdk.EventTypeMessage,
			sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
			sdk.NewAttribute(sdk.AttributeKeyAction, types.EventTypeSolveScavenge),
			sdk.NewAttribute(sdk.AttributeKeySender, msg.Scavenger.String()),
			sdk.NewAttribute(types.AttributeSolutionHash, solutionHashString),
			sdk.NewAttribute(types.AttributeDescription, scavenge.Description),
			sdk.NewAttribute(types.AttributeSolution, msg.Solution),
			sdk.NewAttribute(types.AttributeScavenger, scavenge.Scavenger.String()),
			sdk.NewAttribute(types.AttributeReward, scavenge.Reward.String()),
		),
	)
	return &sdk.Result{Events: ctx.EventManager().Events()}, nil
}

完成后,我们需要在主处理程序中注册以下功能

handler.go
package scavenge

import (
	"fmt"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	"github.com/github-username/scavenge/x/scavenge/keeper"
	"github.com/github-username/scavenge/x/scavenge/types"
)

// NewHandler ...
func NewHandler(k keeper.Keeper) sdk.Handler {
	return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
		ctx = ctx.WithEventManager(sdk.NewEventManager())
		switch msg := msg.(type) {
		// this line is used by starport scaffolding # 1
		case types.MsgSetCommit:
			return handleMsgSetCommit(ctx, k, msg)
		case types.MsgDeleteCommit:
			return handleMsgDeleteCommit(ctx, k, msg)
		case types.MsgCommitSolution:
			return handleMsgCommitSolution(ctx, k, msg)
		case types.MsgCreateScavenge:
			return handleMsgCreateScavenge(ctx, k, msg)
		case types.MsgRevealSolution:
			return handleMsgRevealSolution(ctx, k, msg)
		case types.MsgSetScavenge:
			return handleMsgSetScavenge(ctx, k, msg)
		case types.MsgDeleteScavenge:
			return handleMsgDeleteScavenge(ctx, k, msg)
		default:
			errMsg := fmt.Sprintf("unrecognized %s message type: %T", types.ModuleName, msg)
			return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, errMsg)
		}
	}
}

Events

每个处理程序的末尾是一个EventManager,它将在事务内创建日志,以显示有关在处理此消息期间发生的情况的信息。这对于希望确切了解状态转换结果发生的客户端软件很有用。这些事件使用一系列预定义的类型,可以在./x/scavenge/types/events.go以下类型中找到它们

package types

// scavenge module event types
const (
	// TODO: Create your event types
	// EventType<Action>    		= "action"
	EventTypeCreateScavenge = "CreateScavenge"
	EventTypeCommitSolution = "CommitSolution"
	EventTypeSolveScavenge  = "SolveScavenge"

	// TODO: Create keys fo your events, the values will be derivided from the msg
	// AttributeKeyAddress  		= "address"
	AttributeDescription           = "description"
	AttributeSolution              = "solution"
	AttributeSolutionHash          = "solutionHash"
	AttributeReward                = "reward"
	AttributeScavenger             = "scavenger"
	AttributeSolutionScavengerHash = "solutionScavengerHash"

	// TODO: Some events may not have values for that reason you want to emit that something happened.
	// AttributeValueDoubleSign = "double_sign"

	AttributeValueCategory = ModuleName
)

现在,我们有更新的状态所需的所有部件(MessageHandlerKeeper),我们可能会考虑在其中我们可以如何查询状态。通常,这是通过REST端点和/或CLI完成的。这两个客户端都与查询状态的应用程序部分交互,称为Querier

Querier模块代码修改

为了查询应用程序的数据,我们需要使用来使其可访问Querier。该应用程序的一部分Keeper与访问状态并返回状态一起工作。在Querier中定义./x/scavenge/keeper/querier.go。我们的starport工具为我们提供了一些外观方面的建议,类似于Handler我们想要处理不同查询路线的建议。

您可以Querier针对许多不同类型的查询在内建立许多不同的路由,但我们将只进行三个:

  • listCommit 将列出所有提交
  • getCommit 将得到一个提交 solutionScavengerHash
  • listScavenge 将列出所有清除
  • getScavenge 将会得到一次清除 solutionHash

合并到switch语句中,并充实每个函数,该文件应如下所示

querier.go
package keeper

import (
  // this line is used by starport scaffolding # 1
	"github.com/github-username/scavenge/x/scavenge/types"
		
	
		
	abci "github.com/tendermint/tendermint/abci/types"

	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// NewQuerier creates a new querier for scavenge clients.
func NewQuerier(k Keeper) sdk.Querier {
	return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, error) {
		switch path[0] {
    // this line is used by starport scaffolding # 2
		case types.QueryListCommit:
			return listCommit(ctx, k)
		case types.QueryGetCommit:
			return getCommit(ctx, path[1:], k)
		case types.QueryListScavenge:
			return listScavenge(ctx, k)
		case types.QueryGetScavenge:
			return getScavenge(ctx, path[1:], k)
		default:
			return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown scavenge query endpoint")
		}
	}
}

Types

您可能会注意到,我们在初始switch语句中使用了四种不同的导入类型。这些在我们的./x/scavenge/types/querier.go文件中定义为简单字符串。该文件应如下所示

package types

const (
	QueryListScavenge = "list-scavenge"
	QueryGetScavenge  = "get-scavenge"
)

const (
	QueryListCommit = "list-commit"
	QueryGetCommit  = "get-commit"
)

我们的查询非常简单,因为我们已经Keeper为访问状态配备了所有必需的功能。您也可以在这里看到正在使用的迭代器。

现在,我们已经创建了模块的所有基本操作,我们希望使它们可访问。我们可以使用CLI客户端和REST客户端来做到这一点。在本教程中,我们将创建一个CLI客户端

Cli模块代码修改

命令行界面(CLI)将在应用程序在某处机器上运行后帮助我们与它进行交互。每个模块在CLI内都有自己的名称空间,这使它能够创建和签名要由该模块处理的消息。它还具有查询该模块状态的功能。与该应用程序的其余部分结合使用时,CLI将允许您执行诸如为新帐户生成密钥或检查您已经与该应用程序进行交互的状态之类的操作

我们的模块CLI被分成两个文件名为tx.go以及query.go分别位于./x/scavenge/client/cli/。一个文件用于进行包含消息的事务,这些消息最终将更新我们的状态。另一个是进行查询,这将使我们能够从状态中读取信息

tx.go

tx.go文件包含GetTxCmdCosmos SDK中的标准方法。稍后在module.go文件中引用该文件,该文件准确描述了模块具有的属性。这使得在实际应用程序级别更容易合并出于不同原因的不同模块。毕竟,我们现在将重点放在模块上,但是稍后我们将创建一个利用该模块以及Cosmos SDK中已经可用的其他模块的应用程序。

在内部,GetTxCmd我们创建一个新的模块特定命令并调用它scavenge。在此命令中,我们为定义的每种消息类型添加一个子命令:

  • GetCmdCreateScavenge
  • GetCmdCommitSolution
  • GetCmdRevealSolution

每个函数都从Cobra CLI工具中获取参数以创建一个新的味精,对其进行签名并将其提交给要处理的应用程序。这些函数应该放在tx.gotx<Type>.go文件中,如下所示。

scavenge/x/scavenge/client/cli/tx.go
package cli

import (
	"fmt"

	"github.com/spf13/cobra"

	"github.com/cosmos/cosmos-sdk/client"
	"github.com/cosmos/cosmos-sdk/client/flags"
	"github.com/cosmos/cosmos-sdk/codec"
	"github.com/github-username/scavenge/x/scavenge/types"
)

// GetTxCmd returns the transaction commands for this module
func GetTxCmd(cdc *codec.Codec) *cobra.Command {
	scavengeTxCmd := &cobra.Command{
		Use:                        types.ModuleName,
		Short:                      fmt.Sprintf("%s transactions subcommands", types.ModuleName),
		DisableFlagParsing:         true,
		SuggestionsMinimumDistance: 2,
		RunE:                       client.ValidateCmd,
	}

	scavengeTxCmd.AddCommand(flags.PostCommands(
		// this line is used by starport scaffolding # 1
		GetCmdCommitSolution(cdc),
		GetCmdRevealSolution(cdc),
		GetCmdSetCommit(cdc),
		GetCmdDeleteCommit(cdc),
		GetCmdCreateScavenge(cdc),
		GetCmdSetScavenge(cdc),
		GetCmdDeleteScavenge(cdc),
	)...)

	return scavengeTxCmd
}

scavenge/x/scavenge/client/cli/txScavenge.go
package cli

import (
	"bufio"
	"crypto/sha256"
	"encoding/hex"

	"github.com/spf13/cobra"

	"github.com/cosmos/cosmos-sdk/client/context"
	"github.com/cosmos/cosmos-sdk/codec"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/cosmos/cosmos-sdk/x/auth"
	"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
	"github.com/github-username/scavenge/x/scavenge/types"
)

func GetCmdCreateScavenge(cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "create-scavenge [description] [solution] [reward]",
		Short: "Creates a new scavenge",
		Args:  cobra.ExactArgs(3),
		RunE: func(cmd *cobra.Command, args []string) error {

			cliCtx := context.NewCLIContext().WithCodec(cdc)
			inBuf := bufio.NewReader(cmd.InOrStdin())
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))

			var solution = args[1]
			var solutionHash = sha256.Sum256([]byte(solution))
			var solutionHashString = hex.EncodeToString(solutionHash[:])

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

			msg := types.NewMsgCreateScavenge(cliCtx.GetFromAddress(), args[0], solutionHashString, reward)
			err = msg.ValidateBasic()
			if err != nil {
				return err
			}

			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
		},
	}
}

func GetCmdRevealSolution(cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "reveal-solution [solution]",
		Short: "Reveals a solution for scavenge",
		Args:  cobra.ExactArgs(1), // Does your request require arguments
		RunE: func(cmd *cobra.Command, args []string) error {

			cliCtx := context.NewCLIContext().WithCodec(cdc)
			inBuf := bufio.NewReader(cmd.InOrStdin())
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))

			var solution = args[0]

			msg := types.NewMsgRevealSolution(cliCtx.GetFromAddress(), solution)
			err := msg.ValidateBasic()
			if err != nil {
				return err
			}

			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
		},
	}
}

func GetCmdSetScavenge(cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "set-scavenge [description] [solutionHash] [reward] [solution] [scavenger]",
		Short: "Set a new scavenge",
		Args:  cobra.ExactArgs(5),
		RunE: func(cmd *cobra.Command, args []string) error {
			argsDescription := string(args[0])
			argsSolutionHash := string(args[1])
			argsReward := string(args[2])
			argsSolution := string(args[3])
			argsScavenger := string(args[4])

			cliCtx := context.NewCLIContext().WithCodec(cdc)
			inBuf := bufio.NewReader(cmd.InOrStdin())
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
			msg := types.NewMsgSetScavenge(cliCtx.GetFromAddress(), string(argsDescription), string(argsSolutionHash), string(argsReward), string(argsSolution), sdk.AccAddress(argsScavenger))
			err := msg.ValidateBasic()
			if err != nil {
				return err
			}
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
		},
	}
}

func GetCmdDeleteScavenge(cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "delete-scavenge solutionHash",
		Short: "Delete a new scavenge by solutionHash",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {

			cliCtx := context.NewCLIContext().WithCodec(cdc)
			inBuf := bufio.NewReader(cmd.InOrStdin())
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))

			msg := types.NewMsgDeleteScavenge(args[0], cliCtx.GetFromAddress())
			err := msg.ValidateBasic()
			if err != nil {
				return err
			}
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
		},
	}
}

scavenge/x/scavenge/client/cli/txCommit.go
package cli

import (
	"bufio"
	"crypto/sha256"
	"encoding/hex"

	"github.com/spf13/cobra"

	"github.com/cosmos/cosmos-sdk/client/context"
	"github.com/cosmos/cosmos-sdk/codec"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/cosmos/cosmos-sdk/x/auth"
	"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
	"github.com/github-username/scavenge/x/scavenge/types"
)

func GetCmdCommitSolution(cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "commit-solution [solution]",
		Short: "Commits a solution for scavenge",
		Args:  cobra.ExactArgs(1), // Does your request require arguments
		RunE: func(cmd *cobra.Command, args []string) error {

			cliCtx := context.NewCLIContext().WithCodec(cdc)
			inBuf := bufio.NewReader(cmd.InOrStdin())
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))

			var solution = args[0]
			var solutionHash = sha256.Sum256([]byte(solution))
			var solutionHashString = hex.EncodeToString(solutionHash[:])

			var scavenger = cliCtx.GetFromAddress().String()

			var solutionScavengerHash = sha256.Sum256([]byte(solution + scavenger))
			var solutionScavengerHashString = hex.EncodeToString(solutionScavengerHash[:])

			msg := types.NewMsgCommitSolution(cliCtx.GetFromAddress(), solutionHashString, solutionScavengerHashString)
			err := msg.ValidateBasic()
			if err != nil {
				return err
			}
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
		},
	}
}

func GetCmdSetCommit(cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "set-commit [id]  [solutionHash] [solutionScavengerHash]",
		Short: "Set a new commit",
		Args:  cobra.ExactArgs(3),
		RunE: func(cmd *cobra.Command, args []string) error {
			id := args[0]
			argsSolutionHash := string(args[1])
			argsSolutionScavengerHash := string(args[2])

			cliCtx := context.NewCLIContext().WithCodec(cdc)
			inBuf := bufio.NewReader(cmd.InOrStdin())
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
			msg := types.NewMsgSetCommit(cliCtx.GetFromAddress(), id, string(argsSolutionHash), string(argsSolutionScavengerHash))
			err := msg.ValidateBasic()
			if err != nil {
				return err
			}
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
		},
	}
}

func GetCmdDeleteCommit(cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "delete-commit [id]",
		Short: "Delete a new commit by ID",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {

			cliCtx := context.NewCLIContext().WithCodec(cdc)
			inBuf := bufio.NewReader(cmd.InOrStdin())
			txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))

			msg := types.NewMsgDeleteCommit(args[0], cliCtx.GetFromAddress())
			err := msg.ValidateBasic()
			if err != nil {
				return err
			}
			return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
		},
	}
}

query.go

query.go文件包含类似的Cobra命令,这些命令保留了一个新的名称空间来引用我们的scavenge模块。但是,query.goquery<Type>.go文件不是创建和提交消息,而是创建查询并以人类可读的形式返回结果。它处理的查询与我们querier.go先前在文件中定义的查询相同:

  • GetCmdListCommit
  • GetCmdGetCommit
  • GetCmdListScavenge
  • GetCmdGetScavenge

定义这些命令后,文件应如下所示

query.go
package cli

import (
	"fmt"
	// "strings"

	"github.com/spf13/cobra"

	"github.com/cosmos/cosmos-sdk/client"
	"github.com/cosmos/cosmos-sdk/client/flags"

	// "github.com/cosmos/cosmos-sdk/client/context"
	"github.com/cosmos/cosmos-sdk/codec"
	// sdk "github.com/cosmos/cosmos-sdk/types"

	"github.com/github-username/scavenge/x/scavenge/types"
)

// GetQueryCmd returns the cli query commands for this module
func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
	// Group scavenge queries under a subcommand
	scavengeQueryCmd := &cobra.Command{
		Use:                        types.ModuleName,
		Short:                      fmt.Sprintf("Querying commands for the %s module", types.ModuleName),
		DisableFlagParsing:         true,
		SuggestionsMinimumDistance: 2,
		RunE:                       client.ValidateCmd,
	}

	scavengeQueryCmd.AddCommand(
		flags.GetCommands(
      // this line is used by starport scaffolding # 1
			GetCmdListCommit(queryRoute, cdc),
			GetCmdGetCommit(queryRoute, cdc),
			GetCmdListScavenge(queryRoute, cdc),
			GetCmdGetScavenge(queryRoute, cdc),
		)...,
	)

	return scavengeQueryCmd
}

queryScavenge.go
package cli

import (
	"fmt"

	"github.com/cosmos/cosmos-sdk/client/context"
	"github.com/cosmos/cosmos-sdk/codec"
	"github.com/github-username/scavenge/x/scavenge/types"
	"github.com/spf13/cobra"
)

func GetCmdListScavenge(queryRoute string, cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "list-scavenge",
		Short: "list all scavenge",
		RunE: func(cmd *cobra.Command, args []string) error {
			cliCtx := context.NewCLIContext().WithCodec(cdc)
			res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/"+types.QueryListScavenge, queryRoute), nil)
			if err != nil {
				fmt.Printf("could not list Scavenge\n%s\n", err.Error())
				return nil
			}
			var out []types.Scavenge
			cdc.MustUnmarshalJSON(res, &out)
			return cliCtx.PrintOutput(out)
		},
	}
}

func GetCmdGetScavenge(queryRoute string, cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "get-scavenge [solutionHash]",
		Short: "Query a scavenge by solutionHash",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			cliCtx := context.NewCLIContext().WithCodec(cdc)
			solutionHash := args[0]

			res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", queryRoute, types.QueryGetScavenge, solutionHash), nil)
			if err != nil {
				fmt.Printf("could not resolve scavenge %s \n%s\n", solutionHash, err.Error())

				return nil
			}

			var out types.Scavenge
			cdc.MustUnmarshalJSON(res, &out)
			return cliCtx.PrintOutput(out)
		},
	}
}
queryCommit.go
package cli

import (
	"fmt"

	"github.com/cosmos/cosmos-sdk/client/context"
	"github.com/cosmos/cosmos-sdk/codec"
	"github.com/spf13/cobra"
    "github.com/github-username/scavenge/x/scavenge/types"
)

func GetCmdListCommit(queryRoute string, cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "list-commit",
		Short: "list all commit",
		RunE: func(cmd *cobra.Command, args []string) error {
			cliCtx := context.NewCLIContext().WithCodec(cdc)
			res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/"+types.QueryListCommit, queryRoute), nil)
			if err != nil {
				fmt.Printf("could not list Commit\n%s\n", err.Error())
				return nil
			}
			var out []types.Commit
			cdc.MustUnmarshalJSON(res, &out)
			return cliCtx.PrintOutput(out)
		},
	}
}

func GetCmdGetCommit(queryRoute string, cdc *codec.Codec) *cobra.Command {
	return &cobra.Command{
		Use:   "get-commit [key]",
		Short: "Query a commit by key",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			cliCtx := context.NewCLIContext().WithCodec(cdc)
			key := args[0]

			res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", queryRoute, types.QueryGetCommit, key), nil)
			if err != nil {
				fmt.Printf("could not resolve commit %s \n%s\n", key, err.Error())

				return nil
			}

			var out types.Commit
			cdc.MustUnmarshalJSON(res, &out)
			return cliCtx.PrintOutput(out)
		},
	}
}
REST
rest/txCommit.go
package rest

import (
	"net/http"
	"strconv"

	"github.com/cosmos/cosmos-sdk/client/context"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/cosmos/cosmos-sdk/types/rest"
	"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
	"github.com/github-username/scavenge/x/scavenge/types"
)

// Used to not have an error if strconv is unused
var _ = strconv.Itoa(42)

type createCommitRequest struct {
	BaseReq               rest.BaseReq `json:"base_req"`
	Scavenger             string       `json:"scavenger"`
	SolutionHash          string       `json:"solutionHash"`
	SolutionScavengerHash string       `json:"solutionScavengerHash"`
}

func createCommitHandler(cliCtx context.CLIContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req createCommitRequest
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
			rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
			return
		}
		baseReq := req.BaseReq.Sanitize()
		if !baseReq.ValidateBasic(w) {
			return
		}
		scavenger, err := sdk.AccAddressFromBech32(req.Scavenger)
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		parsedSolutionHash := req.SolutionHash

		parsedSolutionScavengerHash := req.SolutionScavengerHash

		msg := types.NewMsgCommitSolution(
			scavenger,
			parsedSolutionHash,
			parsedSolutionScavengerHash,
		)

		err = msg.ValidateBasic()
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
	}
}

type setCommitRequest struct {
	BaseReq               rest.BaseReq `json:"base_req"`
	Scavenger             string       `json:"scavenger"`
	SolutionHash          string       `json:"solutionHash"`
	SolutionScavengerHash string       `json:"solutionScavengerHash"`
}

func setCommitHandler(cliCtx context.CLIContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req setCommitRequest
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
			rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
			return
		}
		baseReq := req.BaseReq.Sanitize()
		if !baseReq.ValidateBasic(w) {
			return
		}
		scavenger, err := sdk.AccAddressFromBech32(req.Scavenger)
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		parsedSolutionHash := req.SolutionHash

		parsedSolutionScavengerHash := req.SolutionScavengerHash

		msg := types.NewMsgSetCommit(
			scavenger,
			req.SolutionHash,
			parsedSolutionHash,
			parsedSolutionScavengerHash,
		)

		err = msg.ValidateBasic()
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
	}
}

type deleteCommitRequest struct {
	BaseReq rest.BaseReq `json:"base_req"`
	Creator string       `json:"creator"`
	ID      string       `json:"id"`
}

func deleteCommitHandler(cliCtx context.CLIContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req deleteCommitRequest
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
			rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
			return
		}
		baseReq := req.BaseReq.Sanitize()
		if !baseReq.ValidateBasic(w) {
			return
		}
		creator, err := sdk.AccAddressFromBech32(req.Creator)
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}
		msg := types.NewMsgDeleteCommit(req.ID, creator)

		err = msg.ValidateBasic()
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
	}
}

rest/txScavenge.go
package rest

import (
	"net/http"
	"strconv"

	"github.com/cosmos/cosmos-sdk/client/context"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/cosmos/cosmos-sdk/types/rest"
	"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
	"github.com/github-username/scavenge/x/scavenge/types"
)

// Used to not have an error if strconv is unused
var _ = strconv.Itoa(42)

type createScavengeRequest struct {
	BaseReq      rest.BaseReq   `json:"base_req"`
	Creator      string         `json:"creator"`
	Description  string         `json:"description"`
	SolutionHash string         `json:"solutionHash"`
	Reward       sdk.Coins      `json:"reward"`
	Scavenger    sdk.AccAddress `json:"creator"`
}

func createScavengeHandler(cliCtx context.CLIContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req createScavengeRequest
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
			rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
			return
		}
		baseReq := req.BaseReq.Sanitize()
		if !baseReq.ValidateBasic(w) {
			return
		}
		creator, err := sdk.AccAddressFromBech32(req.Creator)
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		parsedDescription := req.Description

		parsedSolutionHash := req.SolutionHash

		parsedReward := req.Reward

		msg := types.NewMsgCreateScavenge(
			creator,
			parsedDescription,
			parsedSolutionHash,
			parsedReward,
		)

		err = msg.ValidateBasic()
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
	}
}

type setScavengeRequest struct {
	BaseReq      rest.BaseReq   `json:"base_req"`
	ID           string         `json:"id"`
	Creator      string         `json:"creator"`
	Description  string         `json:"description"`
	SolutionHash string         `json:"solutionHash"`
	Reward       string         `json:"reward"`
	Solution     string         `json:"solution"`
	Scavenger    sdk.AccAddress `json:"scavenger"`
}

func setScavengeHandler(cliCtx context.CLIContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req setScavengeRequest
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
			rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
			return
		}
		baseReq := req.BaseReq.Sanitize()
		if !baseReq.ValidateBasic(w) {
			return
		}
		creator, err := sdk.AccAddressFromBech32(req.Creator)
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		parsedDescription := req.Description

		parsedSolutionHash := req.SolutionHash

		parsedReward := req.Reward

		parsedSolution := req.Solution

		parsedScavenger := req.Scavenger

		msg := types.NewMsgSetScavenge(
			creator,
			parsedDescription,
			parsedSolutionHash,
			parsedReward,
			parsedSolution,
			parsedScavenger,
		)

		err = msg.ValidateBasic()
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
	}
}

type deleteScavengeRequest struct {
	BaseReq rest.BaseReq `json:"base_req"`
	Creator string       `json:"creator"`
	ID      string       `json:"id"`
}

func deleteScavengeHandler(cliCtx context.CLIContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var req deleteScavengeRequest
		if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
			rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
			return
		}
		baseReq := req.BaseReq.Sanitize()
		if !baseReq.ValidateBasic(w) {
			return
		}
		creator, err := sdk.AccAddressFromBech32(req.Creator)
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}
		msg := types.NewMsgDeleteScavenge(req.ID, creator)

		err = msg.ValidateBasic()
		if err != nil {
			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
			return
		}

		utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
	}
}

运行游戏

命令行输入

scavengecli tx scavenge create-scavenge "What's brown and sticky?" "A stick" 69token --from user1

参考 https://tutorials.cosmos.network/scavenge/tutorial/11-play.html

名称服务

构建的应用程序的目标是让用户购买名称并设置这些名称解析为的值。给定名称的所有者将是当前出价最高的人

详细构建应用请参考下面链接

https://tutorials.cosmos.network/nameservice/tutorial/01-app-design.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值