From 764fb1f04a195fa172bff8aa33743e94883271aa Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Fri, 16 Feb 2024 15:02:52 +0530 Subject: [PATCH] Add commands to reserve and get name authority --- x/auction/msgs.go | 18 +++- x/registry/keeper/keeper.go | 32 +++++- x/registry/keeper/msg_server.go | 5 +- x/registry/keeper/naming_keeper.go | 162 ++++++++++++++++++++++++++++- x/registry/keeper/query_server.go | 7 +- x/registry/keys.go | 8 +- x/registry/module/autocli.go | 22 +++- 7 files changed, 237 insertions(+), 17 deletions(-) diff --git a/x/auction/msgs.go b/x/auction/msgs.go index 3a3ae2d5..8e8aab2e 100644 --- a/x/auction/msgs.go +++ b/x/auction/msgs.go @@ -6,7 +6,23 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -var _ sdk.Msg = &MsgCommitBid{} +var ( + _ sdk.Msg = &MsgCreateAuction{} + _ sdk.Msg = &MsgCommitBid{} + _ sdk.Msg = &MsgRevealBid{} +) + +// NewMsgCreateAuction is the constructor function for MsgCreateAuction. +func NewMsgCreateAuction(params Params, signer sdk.AccAddress) MsgCreateAuction { + return MsgCreateAuction{ + CommitsDuration: params.CommitsDuration, + RevealsDuration: params.RevealsDuration, + CommitFee: params.CommitFee, + RevealFee: params.RevealFee, + MinimumBid: params.MinimumBid, + Signer: signer.String(), + } +} // NewMsgCommitBid is the constructor function for MsgCommitBid. func NewMsgCommitBid(auctionID string, commitHash string, signer sdk.AccAddress) MsgCommitBid { diff --git a/x/registry/keeper/keeper.go b/x/registry/keeper/keeper.go index 20eef6c7..f93e1436 100644 --- a/x/registry/keeper/keeper.go +++ b/x/registry/keeper/keeper.go @@ -40,7 +40,7 @@ func (b RecordsIndexes) IndexesList() []collections.Index[string, registrytypes. func newRecordIndexes(sb *collections.SchemaBuilder) RecordsIndexes { return RecordsIndexes{ BondId: indexes.NewMulti( - sb, registrytypes.BondIdIndexPrefix, "records_by_bond_id", + sb, registrytypes.RecordsByBondIdIndexPrefix, "records_by_bond_id", collections.StringKey, collections.StringKey, func(_ string, v registrytypes.Record) (string, error) { return v.BondId, nil @@ -49,6 +49,18 @@ func newRecordIndexes(sb *collections.SchemaBuilder) RecordsIndexes { } } +// TODO +type AuthoritiesIndexes struct { +} + +func (b AuthoritiesIndexes) IndexesList() []collections.Index[string, registrytypes.NameAuthority] { + return []collections.Index[string, registrytypes.NameAuthority]{} +} + +func newAuthorityIndexes(sb *collections.SchemaBuilder) AuthoritiesIndexes { + return AuthoritiesIndexes{} +} + type Keeper struct { cdc codec.BinaryCodec @@ -59,9 +71,10 @@ type Keeper struct { auctionKeeper auctionkeeper.Keeper // state management - Schema collections.Schema - Params collections.Item[registrytypes.Params] - Records *collections.IndexedMap[string, registrytypes.Record, RecordsIndexes] + Schema collections.Schema + Params collections.Item[registrytypes.Params] + Records *collections.IndexedMap[string, registrytypes.Record, RecordsIndexes] + Authorities *collections.IndexedMap[string, registrytypes.NameAuthority, AuthoritiesIndexes] } // NewKeeper creates a new Keeper instance @@ -83,7 +96,16 @@ func NewKeeper( bondKeeper: bondKeeper, auctionKeeper: auctionKeeper, Params: collections.NewItem(sb, registrytypes.ParamsPrefix, "params", codec.CollValue[registrytypes.Params](cdc)), - Records: collections.NewIndexedMap(sb, registrytypes.RecordsPrefix, "records", collections.StringKey, codec.CollValue[registrytypes.Record](cdc), newRecordIndexes(sb)), + Records: collections.NewIndexedMap( + sb, registrytypes.RecordsPrefix, "records", + collections.StringKey, codec.CollValue[registrytypes.Record](cdc), + newRecordIndexes(sb), + ), + Authorities: collections.NewIndexedMap( + sb, registrytypes.AuthoritiesPrefix, "authorities", + collections.StringKey, codec.CollValue[registrytypes.NameAuthority](cdc), + newAuthorityIndexes(sb), + ), } schema, err := sb.Build() diff --git a/x/registry/keeper/msg_server.go b/x/registry/keeper/msg_server.go index 6d843fff..b2e42cb0 100644 --- a/x/registry/keeper/msg_server.go +++ b/x/registry/keeper/msg_server.go @@ -77,6 +77,7 @@ func (ms msgServer) SetName(c context.Context, msg *registrytypes.MsgSetName) (* func (ms msgServer) ReserveName(c context.Context, msg *registrytypes.MsgReserveAuthority) (*registrytypes.MsgReserveAuthorityResponse, error) { ctx := sdk.UnwrapSDKContext(c) + _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { return nil, err @@ -85,10 +86,12 @@ func (ms msgServer) ReserveName(c context.Context, msg *registrytypes.MsgReserve if err != nil { return nil, err } - err = ms.k.ProcessReserveAuthority(ctx, *msg) + + err = ms.k.ReserveAuthority(ctx, *msg) if err != nil { return nil, err } + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( registrytypes.EventTypeReserveNameAuthority, diff --git a/x/registry/keeper/naming_keeper.go b/x/registry/keeper/naming_keeper.go index 668b2e4b..b2766333 100644 --- a/x/registry/keeper/naming_keeper.go +++ b/x/registry/keeper/naming_keeper.go @@ -1,14 +1,42 @@ package keeper import ( - sdk "github.com/cosmos/cosmos-sdk/types" + "errors" + "fmt" + "net/url" + "cosmossdk.io/collections" + errorsmod "cosmossdk.io/errors" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + auctiontypes "git.vdb.to/cerc-io/laconic2d/x/auction" registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry" + "git.vdb.to/cerc-io/laconic2d/x/registry/helpers" ) +// HasNameAuthority - checks if a name/authority exists. +func (k Keeper) HasNameAuthority(ctx sdk.Context, name string) (bool, error) { + has, err := k.Authorities.Has(ctx, name) + if err != nil { + return false, err + } + + return has, nil +} + // GetNameAuthority - gets a name authority from the store. -func (k Keeper) GetNameAuthority(ctx sdk.Context, name string) registrytypes.NameAuthority { - panic("unimplemented") +func (k Keeper) GetNameAuthority(ctx sdk.Context, name string) (registrytypes.NameAuthority, error) { + authority, err := k.Authorities.Get(ctx, name) + if err != nil { + if errors.Is(err, collections.ErrNotFound) { + return registrytypes.NameAuthority{}, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Name authority not found.") + } + return registrytypes.NameAuthority{}, err + } + + return authority, nil } // HasNameRecord - checks if a name record exists. @@ -31,9 +59,125 @@ func (k Keeper) ProcessSetName(ctx sdk.Context, msg registrytypes.MsgSetName) er panic("unimplemented") } +// SaveNameAuthority creates the NameAuthority record. +func (k Keeper) SaveNameAuthority(ctx sdk.Context, name string, authority *registrytypes.NameAuthority) error { + return k.Authorities.Set(ctx, name, *authority) + + // TODO + // updateBlockChangeSetForNameAuthority(ctx, codec, store, name) +} + // ProcessReserveAuthority reserves a name authority. -func (k Keeper) ProcessReserveAuthority(ctx sdk.Context, msg registrytypes.MsgReserveAuthority) error { - panic("unimplemented") +func (k Keeper) ReserveAuthority(ctx sdk.Context, msg registrytypes.MsgReserveAuthority) error { + crn := fmt.Sprintf("crn://%s", msg.GetName()) + parsedCrn, err := url.Parse(crn) + if err != nil { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid name") + } + + name := parsedCrn.Host + if fmt.Sprintf("crn://%s", name) != crn { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid name") + } + + // TODO + // if strings.Contains(name, ".") { + // return k.ProcessReserveSubAuthority(ctx, name, msg) + // } + + err = k.createAuthority(ctx, name, msg.GetSigner(), true) + if err != nil { + return err + } + + return nil +} + +func (k Keeper) createAuthority(ctx sdk.Context, name string, owner string, isRoot bool) error { + moduleParams, err := k.GetParams(ctx) + if err != nil { + return err + } + + has, err := k.HasNameAuthority(ctx, name) + if err != nil { + return err + } + if has { + authority, err := k.GetNameAuthority(ctx, name) + if err != nil { + return err + } + + if authority.Status != registrytypes.AuthorityExpired { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Name already reserved.") + } + } + + ownerAddress, err := sdk.AccAddressFromBech32(owner) + if err != nil { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid owner address.") + } + ownerAccount := k.accountKeeper.GetAccount(ctx, ownerAddress) + if ownerAccount == nil { + return errorsmod.Wrap(sdkerrors.ErrUnknownAddress, "Owner account not found.") + } + + authority := registrytypes.NameAuthority{ + OwnerPublicKey: getAuthorityPubKey(ownerAccount.GetPubKey()), + OwnerAddress: owner, + Height: uint64(ctx.BlockHeight()), + Status: registrytypes.AuthorityActive, + AuctionId: "", + BondId: "", + ExpiryTime: ctx.BlockTime().Add(moduleParams.AuthorityGracePeriod), + } + + if isRoot && moduleParams.AuthorityAuctionEnabled { + // If auctions are enabled, clear out owner fields. They will be set after a winner is picked. + authority.OwnerAddress = "" + authority.OwnerPublicKey = "" + // Reset bond ID if required. + if authority.BondId != "" || len(authority.BondId) != 0 { + // TODO + // k.RemoveBondToAuthorityIndexEntry(ctx, authority.BondId, name) + authority.BondId = "" + } + + params := auctiontypes.Params{ + CommitsDuration: moduleParams.AuthorityAuctionCommitsDuration, + RevealsDuration: moduleParams.AuthorityAuctionRevealsDuration, + CommitFee: moduleParams.AuthorityAuctionCommitFee, + RevealFee: moduleParams.AuthorityAuctionRevealFee, + MinimumBid: moduleParams.AuthorityAuctionMinimumBid, + } + + // Create an auction. + msg := auctiontypes.NewMsgCreateAuction(params, ownerAddress) + + auction, sdkErr := k.auctionKeeper.CreateAuction(ctx, msg) + if sdkErr != nil { + return sdkErr + } + + // TODO + // Create auction ID -> authority name index. + // k.AddAuctionToAuthorityMapping(ctx, auction.Id, name) + + authority.Status = registrytypes.AuthorityUnderAuction + authority.AuctionId = auction.Id + authority.ExpiryTime = auction.RevealsEndTime.Add(moduleParams.AuthorityGracePeriod) + } + + // Save name authority in store. + if err = k.SaveNameAuthority(ctx, name, &authority); err != nil { + return err + } + + // TODO + // k.InsertAuthorityExpiryQueue(ctx, name, authority.ExpiryTime) + + return nil } func (k Keeper) ProcessSetAuthorityBond(ctx sdk.Context, msg registrytypes.MsgSetAuthorityBond) error { @@ -53,3 +197,11 @@ func (k Keeper) GetAuthorityExpiryQueue(ctx sdk.Context) []*registrytypes.Expiry func (k Keeper) ResolveCRN(ctx sdk.Context, crn string) *registrytypes.Record { panic("unimplemented") } + +func getAuthorityPubKey(pubKey cryptotypes.PubKey) string { + if pubKey == nil { + return "" + } + + return helpers.BytesToBase64(pubKey.Bytes()) +} diff --git a/x/registry/keeper/query_server.go b/x/registry/keeper/query_server.go index bada886a..3a3b0e20 100644 --- a/x/registry/keeper/query_server.go +++ b/x/registry/keeper/query_server.go @@ -104,7 +104,12 @@ func (qs queryServer) NameRecords(c context.Context, _ *registrytypes.QueryNameR func (qs queryServer) Whois(c context.Context, request *registrytypes.QueryWhoisRequest) (*registrytypes.QueryWhoisResponse, error) { ctx := sdk.UnwrapSDKContext(c) - nameAuthority := qs.k.GetNameAuthority(ctx, request.GetName()) + + nameAuthority, err := qs.k.GetNameAuthority(ctx, request.GetName()) + if err != nil { + return nil, err + } + return ®istrytypes.QueryWhoisResponse{NameAuthority: nameAuthority}, nil } diff --git a/x/registry/keys.go b/x/registry/keys.go index 24100bc9..0f6c57cd 100644 --- a/x/registry/keys.go +++ b/x/registry/keys.go @@ -18,6 +18,10 @@ var ( // ParamsKey is the prefix for params key ParamsPrefix = collections.NewPrefix(0) - RecordsPrefix = collections.NewPrefix(1) - BondIdIndexPrefix = collections.NewPrefix(2) + RecordsPrefix = collections.NewPrefix(1) + RecordsByBondIdIndexPrefix = collections.NewPrefix(2) + + AuthoritiesPrefix = collections.NewPrefix(3) + AuthoritiesByAuctionIdIndexPrefix = collections.NewPrefix(4) + AuthoritiesByBondIdIndexPrefix = collections.NewPrefix(5) ) diff --git a/x/registry/module/autocli.go b/x/registry/module/autocli.go index ba60a563..343d1f5d 100644 --- a/x/registry/module/autocli.go +++ b/x/registry/module/autocli.go @@ -43,11 +43,29 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { {ProtoField: "id"}, }, }, + { + RpcMethod: "Whois", + Use: "whois [name]", + Short: "Get name authority info", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "name"}, + }, + }, }, }, Tx: &autocliv1.ServiceCommandDescriptor{ - Service: registryv1.Msg_ServiceDesc.ServiceName, - RpcCommandOptions: []*autocliv1.RpcCommandOptions{}, + Service: registryv1.Msg_ServiceDesc.ServiceName, + RpcCommandOptions: []*autocliv1.RpcCommandOptions{ + { + RpcMethod: "ReserveName", + Use: "reserve-name [name] [owner]", + Short: "Reserve name", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "name"}, + {ProtoField: "owner"}, + }, + }, + }, EnhanceCustomCommand: true, // Allow additional manual commands }, }