From 6e72f5861673a64a21783376f137b31647fd1939 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Mon, 12 Feb 2024 10:03:39 +0530 Subject: [PATCH] Add commands to create and list auctions --- x/auction/events.go | 19 +++++ x/auction/keeper/keeper.go | 125 +++++++++++++++++++++++++++++-- x/auction/keeper/msg_server.go | 108 ++++++++++++++++++++++++-- x/auction/keeper/query_server.go | 62 ++++++++++++++- x/auction/keys.go | 3 + x/auction/module/autocli.go | 38 +++++++++- x/auction/module/module.go | 4 +- x/auction/types.go | 65 ++++++++++++++++ x/bond/keeper/msg_server.go | 4 +- 9 files changed, 408 insertions(+), 20 deletions(-) create mode 100644 x/auction/events.go create mode 100644 x/auction/types.go diff --git a/x/auction/events.go b/x/auction/events.go new file mode 100644 index 00000000..af130741 --- /dev/null +++ b/x/auction/events.go @@ -0,0 +1,19 @@ +package auction + +const ( + EventTypeCreateAuction = "create-auction" + EventTypeCommitBid = "commit-bid" + EventTypeRevealBid = "reveal-bid" + + AttributeKeyCommitsDuration = "commits-duration" + AttributeKeyRevealsDuration = "reveals-duration" + AttributeKeyCommitFee = "commit-fee" + AttributeKeyRevealFee = "reveal-fee" + AttributeKeyMinimumBid = "minimum-bid" + AttributeKeySigner = "signer" + AttributeKeyAuctionID = "auction-id" + AttributeKeyCommitHash = "commit-hash" + AttributeKeyReveal = "reveal" + + AttributeValueCategory = ModuleName +) diff --git a/x/auction/keeper/keeper.go b/x/auction/keeper/keeper.go index 6f993faf..7e6c59b3 100644 --- a/x/auction/keeper/keeper.go +++ b/x/auction/keeper/keeper.go @@ -2,15 +2,41 @@ package keeper import ( "cosmossdk.io/collections" + "cosmossdk.io/collections/indexes" storetypes "cosmossdk.io/core/store" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" auth "github.com/cosmos/cosmos-sdk/x/auth/keeper" bank "github.com/cosmos/cosmos-sdk/x/bank/keeper" auctiontypes "git.vdb.to/cerc-io/laconic2d/x/auction" ) +type AuctionsIndexes struct { + Owner *indexes.Multi[string, string, auctiontypes.Auction] +} + +func (a AuctionsIndexes) IndexesList() []collections.Index[string, auctiontypes.Auction] { + return []collections.Index[string, auctiontypes.Auction]{a.Owner} +} + +func newAuctionIndexes(sb *collections.SchemaBuilder) AuctionsIndexes { + return AuctionsIndexes{ + Owner: indexes.NewMulti( + sb, auctiontypes.AuctionOwnerIndexPrefix, "auctions_by_owner", + collections.StringKey, collections.StringKey, + func(_ string, v auctiontypes.Auction) (string, error) { + return v.OwnerAddress, nil + }, + ), + } +} + +// TODO: Add required methods + type Keeper struct { // Codecs cdc codec.BinaryCodec @@ -23,10 +49,9 @@ type Keeper struct { // usageKeepers []types.AuctionUsageKeeper // state management - Schema collections.Schema - Params collections.Item[auctiontypes.Params] - // TODO - // Auctions ... + Schema collections.Schema + Params collections.Item[auctiontypes.Params] + Auctions *collections.IndexedMap[string, auctiontypes.Auction, AuctionsIndexes] } // NewKeeper creates a new Keeper instance @@ -42,7 +67,8 @@ func NewKeeper( accountKeeper: accountKeeper, bankKeeper: bankKeeper, Params: collections.NewItem(sb, auctiontypes.ParamsKeyPrefix, "params", codec.CollValue[auctiontypes.Params](cdc)), - // Auctions: ... + Auctions: collections.NewIndexedMap(sb, auctiontypes.AuctionsKeyPrefix, "auctions", collections.StringKey, codec.CollValue[auctiontypes.Auction](cdc), newAuctionIndexes(sb)), + // usageKeepers: usageKeepers, } schema, err := sb.Build() @@ -58,3 +84,92 @@ func NewKeeper( // func (k *Keeper) SetUsageKeepers(usageKeepers []types.AuctionUsageKeeper) { // k.usageKeepers = usageKeepers // } + +// SaveAuction - saves a auction to the store. +func (k Keeper) SaveAuction(ctx sdk.Context, auction *auctiontypes.Auction) error { + return k.Auctions.Set(ctx, auction.Id, *auction) + + // // Notify interested parties. + // for _, keeper := range k.usageKeepers { + // keeper.OnAuction(ctx, auction.Id) + // } + // return nil +} + +// ListAuctions - get all auctions. +func (k Keeper) ListAuctions(ctx sdk.Context) ([]auctiontypes.Auction, error) { + var auctions []auctiontypes.Auction + + iter, err := k.Auctions.Iterate(ctx, nil) + if err != nil { + return nil, err + } + + for ; iter.Valid(); iter.Next() { + auction, err := iter.Value() + if err != nil { + return nil, err + } + + auctions = append(auctions, auction) + } + + return auctions, nil +} + +// CreateAuction creates a new auction. +func (k Keeper) CreateAuction(ctx sdk.Context, msg auctiontypes.MsgCreateAuction) (*auctiontypes.Auction, error) { + // TODO: Setup checks + // Might be called from another module directly, always validate. + // err := msg.ValidateBasic() + // if err != nil { + // return nil, err + // } + + signerAddress, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, err + } + + // Generate auction Id. + account := k.accountKeeper.GetAccount(ctx, signerAddress) + if account == nil { + return nil, errorsmod.Wrap(sdkerrors.ErrInvalidAddress, "Account not found.") + } + + auctionId := auctiontypes.AuctionId{ + Address: signerAddress, + AccNum: account.GetAccountNumber(), + Sequence: account.GetSequence(), + }.Generate() + + // Compute timestamps. + now := ctx.BlockTime() + commitsEndTime := now.Add(msg.CommitsDuration) + revealsEndTime := now.Add(msg.CommitsDuration + msg.RevealsDuration) + + auction := auctiontypes.Auction{ + Id: auctionId, + Status: auctiontypes.AuctionStatusCommitPhase, + OwnerAddress: signerAddress.String(), + CreateTime: now, + CommitsEndTime: commitsEndTime, + RevealsEndTime: revealsEndTime, + CommitFee: msg.CommitFee, + RevealFee: msg.RevealFee, + MinimumBid: msg.MinimumBid, + } + + // Save auction in store. + k.SaveAuction(ctx, &auction) + + return &auction, nil +} + +func (k Keeper) CommitBid(ctx sdk.Context, msg auctiontypes.MsgCommitBid) (*auctiontypes.Bid, error) { + panic("unimplemented") +} + +func (k Keeper) RevealBid(ctx sdk.Context, msg auctiontypes.MsgRevealBid) (*auctiontypes.Auction, error) { + panic("unimplemented") +} diff --git a/x/auction/keeper/msg_server.go b/x/auction/keeper/msg_server.go index c823d585..1339c206 100644 --- a/x/auction/keeper/msg_server.go +++ b/x/auction/keeper/msg_server.go @@ -1,14 +1,112 @@ package keeper -// TODO: Add required read methods +import ( + "context" -// var _ auctiontypes.MsgServer = msgServer{} + auctiontypes "git.vdb.to/cerc-io/laconic2d/x/auction" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var _ auctiontypes.MsgServer = msgServer{} type msgServer struct { k Keeper } // NewMsgServerImpl returns an implementation of the module MsgServer interface. -// func NewMsgServerImpl(keeper Keeper) auctiontypes.MsgServer { -// return &msgServer{k: keeper} -// } +func NewMsgServerImpl(keeper Keeper) auctiontypes.MsgServer { + return &msgServer{k: keeper} +} + +func (ms msgServer) CreateAuction(c context.Context, msg *auctiontypes.MsgCreateAuction) (*auctiontypes.MsgCreateAuctionResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + + signerAddress, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, err + } + + resp, err := ms.k.CreateAuction(ctx, *msg) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + auctiontypes.EventTypeCreateAuction, + sdk.NewAttribute(auctiontypes.AttributeKeyCommitsDuration, msg.CommitsDuration.String()), + sdk.NewAttribute(auctiontypes.AttributeKeyCommitFee, msg.CommitFee.String()), + sdk.NewAttribute(auctiontypes.AttributeKeyRevealFee, msg.RevealFee.String()), + sdk.NewAttribute(auctiontypes.AttributeKeyMinimumBid, msg.MinimumBid.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, auctiontypes.AttributeValueCategory), + sdk.NewAttribute(auctiontypes.AttributeKeySigner, signerAddress.String()), + ), + }) + + return &auctiontypes.MsgCreateAuctionResponse{Auction: resp}, nil +} + +// CommitBid is the command for committing a bid +// nolint: all +func (ms msgServer) CommitBid(c context.Context, msg *auctiontypes.MsgCommitBid) (*auctiontypes.MsgCommitBidResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + + signerAddress, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, err + } + + resp, err := ms.k.CommitBid(ctx, *msg) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + auctiontypes.EventTypeCommitBid, + sdk.NewAttribute(auctiontypes.AttributeKeyAuctionID, msg.AuctionId), + sdk.NewAttribute(auctiontypes.AttributeKeyCommitHash, msg.CommitHash), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, auctiontypes.AttributeValueCategory), + sdk.NewAttribute(auctiontypes.AttributeKeySigner, signerAddress.String()), + ), + }) + + return &auctiontypes.MsgCommitBidResponse{Bid: resp}, nil +} + +// RevealBid is the command for revealing a bid +// nolint: all +func (ms msgServer) RevealBid(c context.Context, msg *auctiontypes.MsgRevealBid) (*auctiontypes.MsgRevealBidResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + + signerAddress, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return nil, err + } + + resp, err := ms.k.RevealBid(ctx, *msg) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + auctiontypes.EventTypeRevealBid, + sdk.NewAttribute(auctiontypes.AttributeKeyAuctionID, msg.AuctionId), + sdk.NewAttribute(auctiontypes.AttributeKeyReveal, msg.Reveal), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, auctiontypes.AttributeValueCategory), + sdk.NewAttribute(auctiontypes.AttributeKeySigner, signerAddress.String()), + ), + }) + + return &auctiontypes.MsgRevealBidResponse{Auction: resp}, nil +} diff --git a/x/auction/keeper/query_server.go b/x/auction/keeper/query_server.go index 5c52f590..5c388e0d 100644 --- a/x/auction/keeper/query_server.go +++ b/x/auction/keeper/query_server.go @@ -1,14 +1,68 @@ package keeper +import ( + "context" + + auctiontypes "git.vdb.to/cerc-io/laconic2d/x/auction" + sdk "github.com/cosmos/cosmos-sdk/types" +) + // TODO: Add required read methods -// var _ auctiontypes.QueryServer = queryServer{} +var _ auctiontypes.QueryServer = queryServer{} type queryServer struct { k Keeper } // NewQueryServerImpl returns an implementation of the module QueryServer. -// func NewQueryServerImpl(k Keeper) auctiontypes.QueryServer { -// return queryServer{k} -// } +func NewQueryServerImpl(k Keeper) auctiontypes.QueryServer { + return queryServer{k} +} + +// Params implements the params query command +func (qs queryServer) Params(c context.Context, req *auctiontypes.QueryParamsRequest) (*auctiontypes.QueryParamsResponse, error) { + panic("unimplemented") +} + +// Auctions queries all auctions +func (qs queryServer) Auctions(c context.Context, req *auctiontypes.QueryAuctionsRequest) (*auctiontypes.QueryAuctionsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + + resp, err := qs.k.ListAuctions(ctx) + if err != nil { + return nil, err + } + + return &auctiontypes.QueryAuctionsResponse{Auctions: &auctiontypes.Auctions{Auctions: resp}}, nil +} + +// GetAuction queries an auction +func (qs queryServer) GetAuction(c context.Context, req *auctiontypes.QueryAuctionRequest) (*auctiontypes.QueryAuctionResponse, error) { + panic("unimplemented") +} + +// GetBid queries and auction bid +func (qs queryServer) GetBid(c context.Context, req *auctiontypes.QueryBidRequest) (*auctiontypes.QueryBidResponse, error) { + panic("unimplemented") +} + +// GetBids queries all auction bids +func (qs queryServer) GetBids(c context.Context, req *auctiontypes.QueryBidsRequest) (*auctiontypes.QueryBidsResponse, error) { + panic("unimplemented") +} + +// AuctionsByBidder queries auctions by bidder +func (qs queryServer) AuctionsByBidder(c context.Context, req *auctiontypes.QueryAuctionsByBidderRequest) (*auctiontypes.QueryAuctionsByBidderResponse, error) { + panic("unimplemented") +} + +// AuctionsByOwner queries auctions by owner +func (qs queryServer) AuctionsByOwner(c context.Context, req *auctiontypes.QueryAuctionsByOwnerRequest) (*auctiontypes.QueryAuctionsByOwnerResponse, error) { + panic("unimplemented") +} + +// GetAuctionModuleBalance queries the auction module account balance +func (qs queryServer) GetAuctionModuleBalance(c context.Context, req *auctiontypes.QueryGetAuctionModuleBalanceRequest) (*auctiontypes.QueryGetAuctionModuleBalanceResponse, error) { + panic("unimplemented") +} diff --git a/x/auction/keys.go b/x/auction/keys.go index e835f3c4..f1c0c0a3 100644 --- a/x/auction/keys.go +++ b/x/auction/keys.go @@ -13,4 +13,7 @@ const ( var ( // ParamsKey is the prefix for params key ParamsKeyPrefix = collections.NewPrefix(0) + + AuctionsKeyPrefix = collections.NewPrefix(1) + AuctionOwnerIndexPrefix = collections.NewPrefix(2) ) diff --git a/x/auction/module/autocli.go b/x/auction/module/autocli.go index 7f12797d..99a6e16e 100644 --- a/x/auction/module/autocli.go +++ b/x/auction/module/autocli.go @@ -3,6 +3,8 @@ package module import ( autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" "cosmossdk.io/client/v2/autocli" + + auctionv1 "git.vdb.to/cerc-io/laconic2d/api/cerc/auction/v1" ) var _ autocli.HasAutoCLIConfig = AppModule{} @@ -10,7 +12,39 @@ var _ autocli.HasAutoCLIConfig = AppModule{} // AutoCLIOptions implements the autocli.HasAutoCLIConfig interface. func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { return &autocliv1.ModuleOptions{ - Query: nil, - Tx: nil, + Query: &autocliv1.ServiceCommandDescriptor{ + Service: auctionv1.Query_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "Params", + Use: "params", + Short: "Get the current bond parameters", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{}, + }, + { + RpcMethod: "Auctions", + Use: "list", + Short: "List auctions", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{}, + }, + }, + }, + Tx: &autocliv1.ServiceCommandDescriptor{ + Service: auctionv1.Msg_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "CreateAuction", + Use: "create [commits-duration] [reveals-duration] [commit-fee] [reveal-fee] [minimum-bid]", + Short: "Create an auction", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "commits_duration"}, + {ProtoField: "reveals_duration"}, + {ProtoField: "commit_fee"}, + {ProtoField: "reveal_fee"}, + {ProtoField: "minimum_bid"}, + }, + }, + }, + }, } } diff --git a/x/auction/module/module.go b/x/auction/module/module.go index a6ae06b0..7a258a39 100644 --- a/x/auction/module/module.go +++ b/x/auction/module/module.go @@ -116,8 +116,8 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (am AppModule) RegisterServices(cfg module.Configurator) { // Register servers - // auction.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) - // auction.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper)) + auction.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper)) + auction.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper)) } // appmodule.HasEndBlocker diff --git a/x/auction/types.go b/x/auction/types.go new file mode 100644 index 00000000..93536356 --- /dev/null +++ b/x/auction/types.go @@ -0,0 +1,65 @@ +package auction + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Auction status values. +const ( + // Auction is in commit phase. + AuctionStatusCommitPhase = "commit" + + // Auction is in reveal phase. + AuctionStatusRevealPhase = "reveal" + + // Auction has ended (no reveals allowed). + AuctionStatusExpired = "expired" + + // Auction has completed (winner selected). + AuctionStatusCompleted = "completed" +) + +// Bid status values. +const ( + BidStatusCommitted = "commit" + BidStatusRevealed = "reveal" +) + +// AuctionId simplifies generation of auction IDs. +type AuctionId struct { + Address sdk.Address + AccNum uint64 + Sequence uint64 +} + +// Generate creates the auction ID. +func (auctionId AuctionId) Generate() string { + hasher := sha256.New() + str := fmt.Sprintf("%s:%d:%d", auctionId.Address.String(), auctionId.AccNum, auctionId.Sequence) + hasher.Write([]byte(str)) + return hex.EncodeToString(hasher.Sum(nil)) +} + +func (auction Auction) GetCreateTime() string { + return string(sdk.FormatTimeBytes(auction.CreateTime)) +} + +func (auction Auction) GetCommitsEndTime() string { + return string(sdk.FormatTimeBytes(auction.CommitsEndTime)) +} + +func (auction Auction) GetRevealsEndTime() string { + return string(sdk.FormatTimeBytes(auction.RevealsEndTime)) +} + +func (bid Bid) GetCommitTime() string { + return string(sdk.FormatTimeBytes(bid.CommitTime)) +} + +func (bid Bid) GetRevealTime() string { + return string(sdk.FormatTimeBytes(bid.RevealTime)) +} diff --git a/x/bond/keeper/msg_server.go b/x/bond/keeper/msg_server.go index c004a191..3827e73b 100644 --- a/x/bond/keeper/msg_server.go +++ b/x/bond/keeper/msg_server.go @@ -8,12 +8,12 @@ import ( "git.vdb.to/cerc-io/laconic2d/x/bond" ) +var _ bond.MsgServer = msgServer{} + type msgServer struct { k Keeper } -var _ bond.MsgServer = msgServer{} - // NewMsgServerImpl returns an implementation of the module MsgServer interface. func NewMsgServerImpl(keeper Keeper) bond.MsgServer { return &msgServer{k: keeper}