1

概述

这篇文章主要讲述使用CosmosIgnite-Cli工具快速开发NameService应用。NameService的主要功能是用户可以购买域名,给域名设置可以解析的地址或则值,域名拥有者可以删除域名。

环境安装

  1. 我们使用 Docker 来部署环境,首先编写 DockerFile 镜像文件,命名DockerFile-ubuntu
FROM --platform=linux ubuntu:22.04
ARG BUILDARCH

# Change your versions here
ENV GO_VERSION=1.18.3
ENV IGNITE_VERSION=0.22.1
ENV NODE_VERSION=18.x

ENV LOCAL=/usr/local
ENV GOROOT=$LOCAL/go
ENV HOME=/root
ENV GOPATH=$HOME/go
ENV PATH=$GOROOT/bin:$GOPATH/bin:$PATH

RUN mkdir -p $GOPATH/bin

ENV PACKAGES curl gcc jq
RUN apt-get update
RUN apt-get install -y $PACKAGES

# Install Go
RUN curl -L https://go.dev/dl/go${GO_VERSION}.linux-$BUILDARCH.tar.gz | tar -C $LOCAL -xzf -

# Install Ignite
RUN curl -L https://get.ignite.com/cli@v${IGNITE_VERSION}! | bash

# Install Node
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION} | bash -
RUN apt-get install -y nodejs

EXPOSE 1317 3000 4500 5000 26657

WORKDIR /nameservice
  1. 创建镜像
docker build -f DockerFile-ubuntu . -t ns_i
  1. 创建 Container
docker create --name ns -i -v $(pwd):/nameservice -w /nameservice -p 1317:1317 -p 4500:4500 -p 5000:5000 -p 26657:26657 ns_i
  1. 启动容器
docker start ns
  1. 检查环境
docker exec -it ns ignite version

显示如下内容,则表示环境安装成功。

Ignite CLI version:     v0.22.1

如果环境安装有问题,可以私信我帮你看看!

创建项目

docker exec -it ns ignite scaffold chain nameservice --no-module

这个命令会创建一个nameservice目录,已经实现了基于Cosmos SDK的区块链功能。
然后我们把代码移动到当前的工作目录

mv nameservice/* ./
rm -rf nameservice

创建Module

docker exec -it ns ignite scaffold module nameservice --dep bank

--dep bank表示该模块依赖bank模块

创建Message

根据我们的业务我们需要创建 3 个Message

  • BuyName(Name, Bid) // 购买域名
  • SetName(Name, Value) // 给域名设置值
  • DeleteName(Name) // 删除域名
    我们需要执行以下命令创建这些Message:
docker exec -it ns ignite scaffold message buy-name name bid

buy-name是方法名,(name, bid)是参数

docker exec -it ns ignite scaffold message set-name name value

set-name是方法名,(name, value)是参数

docker exec -it ns ignite scaffold message delete-name name

delete-name是方法名,(name)是参数

创建Types

现在我们需要创建一个储存的数据结构来记录当前域名的归属关系,我们创建一个Whois的结构

docker exec -it ns ignite scaffold map whois name value price owner --no-message
  • whois是 type
  • name是域名
  • value是域名映射的值
  • price是当前域名的价格
  • owner是当前域名的拥有者
  • --no-message表示不需要创建message

实现业务代码

  1. 声明bank下我们需要用到的方法,编辑x/nameservice/types/expected_keepers.go
// x/nameservice/types/expected_keepers.go

package types

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

type BankKeeper interface {
    SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error // 需要添加的方法
    SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error // 需要添加的方法
}
  1. 编辑x/nameservice/keeper/msg_server_buy_name.go文件,实现购买逻辑
// x/nameservice/keeper/msg_server_buy_name.go

package keeper

import (
    "context"

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

    "nameservice/x/nameservice/types"
)

func (k msgServer) BuyName(goCtx context.Context, msg *types.MsgBuyName) (*types.MsgBuyNameResponse, error) {
    ctx := sdk.UnwrapSDKContext(goCtx)

    // 从store中获取域名数据
    whois, isFound := k.GetWhois(ctx, msg.Name)

    // 设置域名购买的初始最低价
    minPrice := sdk.Coins{sdk.NewInt64Coin("token", 10)}

    // 把价格转换成代币
    price, _ := sdk.ParseCoinsNormalized(whois.Price)
    bid, _ := sdk.ParseCoinsNormalized(msg.Bid)

    // 转换地址
    owner, _ := sdk.AccAddressFromBech32(whois.Owner)
    buyer, _ := sdk.AccAddressFromBech32(msg.Creator)

    // 如果当前域名数据已经存在
    if isFound {
        // 如果当前价格大于竞拍价格
        if price.IsAllGT(bid) {
            // 抛出错误
            return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Bid is not high enough")
        }

        // 如果当前价格小于竞拍价格,把购买者的代币转给域名拥有者
        err := k.bankKeeper.SendCoins(ctx, buyer, owner, bid)
        if err != nil {
            return nil, err
        }
    } else { // 如果域名不存在
        // 如果域名购买最低价大于竞拍价格
        if minPrice.IsAllGT(bid) {
            // 抛出错误
            return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Bid is less than min amount")
        }

        // 如果当前价格小于竞拍价格,把购买者的代币转到模块地址
        err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, buyer, types.ModuleName, bid)
        if err != nil {
            return nil, err
        }
    }

    // 更新`whois`的数据
    newWhois := types.Whois{
        Index: msg.Name,
        Name:  msg.Name,
        Value: whois.Value,
        Price: bid.String(),
        Owner: buyer.String(),
    }

    // 储存到store
    k.SetWhois(ctx, newWhois)
    return &types.MsgBuyNameResponse{}, nil
}
  1. 编辑x/nameservice/keeper/msg_server_set_name.go,实现设置域名映射值的逻辑
// x/nameservice/keeper/msg_server_set_name.go

package keeper

import (
    "context"

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

    "nameservice/x/nameservice/types"
)

func (k msgServer) SetName(goCtx context.Context, msg *types.MsgSetName) (*types.MsgSetNameResponse, error) {
    ctx := sdk.UnwrapSDKContext(goCtx)

    // 从store中获取域名数据
    whois, _ := k.GetWhois(ctx, msg.Name)

    // 如果方法调用者不是域名拥有者,则报错
    if !(msg.Creator == whois.Owner) {
        return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Incorrect Owner")
    }

    // 更新whois的记录,设置值
    newWhois := types.Whois{
        Index: msg.Name,
        Name:  msg.Name,
        Value: msg.Value,
        Owner: whois.Owner,
        Price: whois.Price,
    }

    // 储存到store
    k.SetWhois(ctx, newWhois)
    return &types.MsgSetNameResponse{}, nil
}
  1. 编辑x/nameservice/keeper/msg_server_delete_name.go,实现域名删除逻辑
// x/nameservice/keeper/msg_server_delete_name.go

package keeper

import (
    "context"

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

    "nameservice/x/nameservice/types"
)

func (k msgServer) DeleteName(goCtx context.Context, msg *types.MsgDeleteName) (*types.MsgDeleteNameResponse, error) {
    ctx := sdk.UnwrapSDKContext(goCtx)

    // 从store中获取域名数据
    whois, isFound := k.GetWhois(ctx, msg.Name)

    // 如果找不到域名,则报错
    if !isFound {
        return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Name doesn't exist")
    }

    // 如果方法调用者不是域名拥有者,则报错
    if !(whois.Owner == msg.Creator) {
        return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Incorrect Owner")
    }

    // 将域名从store中删除
    k.RemoveWhois(ctx, msg.Name)
    return &types.MsgDeleteNameResponse{}, nil
}

测试

  1. 启动节点
docker exec -it ns ignite chain serve -r

-r表示重置节点数据 2. 测试购买域名

docker exec -it ns nameserviced tx nameservice buy-name foo 20token --from alice
  1. 查询域名数据
docker exec -it ns nameserviced q nameservice list-whois
  1. 测试设置域名值
docker exec -it ns nameserviced tx nameservice set-name foo bar --from alice
  1. 测试删除域名
docker exec -it ns nameserviced tx nameservice delete-name foo --from alice

总结

这篇文章只是告诉大家如何使用Ignite-CLI更加快速的开发Cosmos项目,如果想更深入的理解相关代码,请参考以下链接
官方文档:https://docs.ignite.com/guide...
登链社区文档:https://learnblockchain.cn/do...

如果你不会使用 Docker,也可以自己安装本地环境,参考链接:https://tutorials.cosmos.netw...

我是 Victor,Web3 爱好者!希望帮助更多人进入 Web3 的世界!

如果有任何问题,请私信我!


Victor
1 声望2 粉丝

Web3爱好者,希望把更多的人带入Web3的世界!