概述
這篇文章主要講述使用Cosmos
的Ignite-Cli
工具快速開發(fā)NameService
應(yīng)用饮焦。NameService
的主要功能是用戶可以購(gòu)買域名皇耗,給域名設(shè)置可以解析的地址或則值联喘,域名擁有者可以刪除域名。
環(huán)境安裝
- 我們使用 Docker 來(lái)部署環(huán)境,首先編寫 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
- 創(chuàng)建鏡像
docker build -f DockerFile-ubuntu . -t ns_i
- 創(chuàng)建 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
- 啟動(dòng)容器
docker start ns
- 檢查環(huán)境
docker exec -it ns ignite version
顯示如下內(nèi)容襟铭,則表示環(huán)境安裝成功碌奉。
Ignite CLI version: v0.22.1
如果環(huán)境安裝有問(wèn)題,可以私信我?guī)湍憧纯矗?/p>
創(chuàng)建項(xiàng)目
docker exec -it ns ignite scaffold chain nameservice --no-module
這個(gè)命令會(huì)創(chuàng)建一個(gè)nameservice
目錄寒砖,已經(jīng)實(shí)現(xiàn)了基于Cosmos SDK
的區(qū)塊鏈功能赐劣。
然后我們把代碼移動(dòng)到當(dāng)前的工作目錄
mv nameservice/* ./
rm -rf nameservice
創(chuàng)建Module
docker exec -it ns ignite scaffold module nameservice --dep bank
--dep bank
表示該模塊依賴bank
模塊
創(chuàng)建Message
根據(jù)我們的業(yè)務(wù)我們需要?jiǎng)?chuàng)建 3 個(gè)Message
- BuyName(Name, Bid) // 購(gòu)買域名
- SetName(Name, Value) // 給域名設(shè)置值
- DeleteName(Name) // 刪除域名
我們需要執(zhí)行以下命令創(chuàng)建這些Message
:
docker exec -it ns ignite scaffold message buy-name name bid
buy-name
是方法名,(name, bid)
是參數(shù)
docker exec -it ns ignite scaffold message set-name name value
set-name
是方法名哩都,(name, value)
是參數(shù)
docker exec -it ns ignite scaffold message delete-name name
delete-name
是方法名魁兼,(name)
是參數(shù)
創(chuàng)建Types
現(xiàn)在我們需要?jiǎng)?chuàng)建一個(gè)儲(chǔ)存的數(shù)據(jù)結(jié)構(gòu)來(lái)記錄當(dāng)前域名的歸屬關(guān)系,我們創(chuàng)建一個(gè)Whois
的結(jié)構(gòu)
docker exec -it ns ignite scaffold map whois name value price owner --no-message
-
whois
是 type -
name
是域名 -
value
是域名映射的值 -
price
是當(dāng)前域名的價(jià)格 -
owner
是當(dāng)前域名的擁有者 -
--no-message
表示不需要?jiǎng)?chuàng)建message
實(shí)現(xiàn)業(yè)務(wù)代碼
- 聲明
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 // 需要添加的方法
}
- 編輯
x/nameservice/keeper/msg_server_buy_name.go
文件咐汞,實(shí)現(xiàn)購(gòu)買邏輯
// 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中獲取域名數(shù)據(jù)
whois, isFound := k.GetWhois(ctx, msg.Name)
// 設(shè)置域名購(gòu)買的初始最低價(jià)
minPrice := sdk.Coins{sdk.NewInt64Coin("token", 10)}
// 把價(jià)格轉(zhuǎn)換成代幣
price, _ := sdk.ParseCoinsNormalized(whois.Price)
bid, _ := sdk.ParseCoinsNormalized(msg.Bid)
// 轉(zhuǎn)換地址
owner, _ := sdk.AccAddressFromBech32(whois.Owner)
buyer, _ := sdk.AccAddressFromBech32(msg.Creator)
// 如果當(dāng)前域名數(shù)據(jù)已經(jīng)存在
if isFound {
// 如果當(dāng)前價(jià)格大于競(jìng)拍價(jià)格
if price.IsAllGT(bid) {
// 拋出錯(cuò)誤
return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Bid is not high enough")
}
// 如果當(dāng)前價(jià)格小于競(jìng)拍價(jià)格盖呼,把購(gòu)買者的代幣轉(zhuǎn)給域名擁有者
err := k.bankKeeper.SendCoins(ctx, buyer, owner, bid)
if err != nil {
return nil, err
}
} else { // 如果域名不存在
// 如果域名購(gòu)買最低價(jià)大于競(jìng)拍價(jià)格
if minPrice.IsAllGT(bid) {
// 拋出錯(cuò)誤
return nil, sdkerrors.Wrap(sdkerrors.ErrInsufficientFunds, "Bid is less than min amount")
}
// 如果當(dāng)前價(jià)格小于競(jìng)拍價(jià)格,把購(gòu)買者的代幣轉(zhuǎn)到模塊地址
err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, buyer, types.ModuleName, bid)
if err != nil {
return nil, err
}
}
// 更新`whois`的數(shù)據(jù)
newWhois := types.Whois{
Index: msg.Name,
Name: msg.Name,
Value: whois.Value,
Price: bid.String(),
Owner: buyer.String(),
}
// 儲(chǔ)存到store
k.SetWhois(ctx, newWhois)
return &types.MsgBuyNameResponse{}, nil
}
- 編輯
x/nameservice/keeper/msg_server_set_name.go
化撕,實(shí)現(xiàn)設(shè)置域名映射值的邏輯
// 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中獲取域名數(shù)據(jù)
whois, _ := k.GetWhois(ctx, msg.Name)
// 如果方法調(diào)用者不是域名擁有者几晤,則報(bào)錯(cuò)
if !(msg.Creator == whois.Owner) {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Incorrect Owner")
}
// 更新whois的記錄,設(shè)置值
newWhois := types.Whois{
Index: msg.Name,
Name: msg.Name,
Value: msg.Value,
Owner: whois.Owner,
Price: whois.Price,
}
// 儲(chǔ)存到store
k.SetWhois(ctx, newWhois)
return &types.MsgSetNameResponse{}, nil
}
- 編輯
x/nameservice/keeper/msg_server_delete_name.go
植阴,實(shí)現(xiàn)域名刪除邏輯
// 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中獲取域名數(shù)據(jù)
whois, isFound := k.GetWhois(ctx, msg.Name)
// 如果找不到域名蟹瘾,則報(bào)錯(cuò)
if !isFound {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "Name doesn't exist")
}
// 如果方法調(diào)用者不是域名擁有者,則報(bào)錯(cuò)
if !(whois.Owner == msg.Creator) {
return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "Incorrect Owner")
}
// 將域名從store中刪除
k.RemoveWhois(ctx, msg.Name)
return &types.MsgDeleteNameResponse{}, nil
}
測(cè)試
- 啟動(dòng)節(jié)點(diǎn)
docker exec -it ns ignite chain serve -r
-r
表示重置節(jié)點(diǎn)數(shù)據(jù) 2. 測(cè)試購(gòu)買域名
docker exec -it ns nameserviced tx nameservice buy-name foo 20token --from alice
- 查詢域名數(shù)據(jù)
docker exec -it ns nameserviced q nameservice list-whois
- 測(cè)試設(shè)置域名值
docker exec -it ns nameserviced tx nameservice set-name foo bar --from alice
- 測(cè)試刪除域名
docker exec -it ns nameserviced tx nameservice delete-name foo --from alice
總結(jié)
這篇文章只是告訴大家如何使用Ignite-CLI
更加快速的開發(fā)Cosmos
項(xiàng)目掠手,如果想更深入的理解相關(guān)代碼憾朴,請(qǐng)參考以下鏈接
官方文檔:https://docs.ignite.com/guide/nameservice
登鏈社區(qū)文檔:https://learnblockchain.cn/docs/cosmos/tutorial/#%E5%BC%80%E5%A7%8B
如果你不會(huì)使用 Docker,也可以自己安裝本地環(huán)境喷鸽,參考鏈接:https://tutorials.cosmos.network/hands-on-exercise/1-ignite-cli/1-ignitecli.html