From 91a8b561833080aa243207aca634f70b2aa0eac0 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Fri, 16 Feb 2024 12:18:43 +0530 Subject: [PATCH 01/13] Add a command to get records by bond id --- x/registry/keeper/keeper.go | 30 ++++++++++++++++++++---------- x/registry/keeper/query_server.go | 9 +++++++-- x/registry/keeper/record_keeper.go | 5 ----- x/registry/module/autocli.go | 8 ++++++++ 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/x/registry/keeper/keeper.go b/x/registry/keeper/keeper.go index 34ec0e3e..20eef6c7 100644 --- a/x/registry/keeper/keeper.go +++ b/x/registry/keeper/keeper.go @@ -106,16 +106,6 @@ func (k Keeper) HasRecord(ctx sdk.Context, id string) (bool, error) { return has, nil } -// GetRecord - gets a record from the store. -func (k Keeper) GetRecord(ctx sdk.Context, id string) (registrytypes.Record, error) { - record, err := k.Records.Get(ctx, id) - if err != nil { - return registrytypes.Record{}, err - } - - return record, nil -} - // ListRecords - get all records. func (k Keeper) ListRecords(ctx sdk.Context) ([]registrytypes.Record, error) { iter, err := k.Records.Iterate(ctx, nil) @@ -129,6 +119,26 @@ func (k Keeper) ListRecords(ctx sdk.Context) ([]registrytypes.Record, error) { return iter.Values() } +// GetRecordById - gets a record from the store. +func (k Keeper) GetRecordById(ctx sdk.Context, id string) (registrytypes.Record, error) { + record, err := k.Records.Get(ctx, id) + if err != nil { + return registrytypes.Record{}, err + } + + return record, nil +} + +// GetRecordsByBondId - gets a record from the store. +func (k Keeper) GetRecordsByBondId(ctx sdk.Context, bondId string) ([]registrytypes.Record, error) { + iter, err := k.Records.Indexes.BondId.MatchExact(ctx, bondId) + if err != nil { + return []registrytypes.Record{}, err + } + + return indexes.CollectValues(ctx, k.Records, iter) +} + // RecordsFromAttributes gets a list of records whose attributes match all provided values func (k Keeper) RecordsFromAttributes(ctx sdk.Context, attributes []*registrytypes.QueryRecordsRequest_KeyValueInput, all bool) ([]registrytypes.Record, error) { panic("unimplemented") diff --git a/x/registry/keeper/query_server.go b/x/registry/keeper/query_server.go index 32c6ce2d..bada886a 100644 --- a/x/registry/keeper/query_server.go +++ b/x/registry/keeper/query_server.go @@ -67,7 +67,7 @@ func (qs queryServer) GetRecord(c context.Context, req *registrytypes.QueryRecor return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "Record not found.") } - record, err := qs.k.GetRecord(ctx, id) + record, err := qs.k.GetRecordById(ctx, id) if err != nil { return nil, err } @@ -77,7 +77,12 @@ func (qs queryServer) GetRecord(c context.Context, req *registrytypes.QueryRecor func (qs queryServer) GetRecordsByBondId(c context.Context, req *registrytypes.QueryRecordsByBondIdRequest) (*registrytypes.QueryRecordsByBondIdResponse, error) { ctx := sdk.UnwrapSDKContext(c) - records := qs.k.recordKeeper.QueryRecordsByBond(ctx, req.GetId()) + + records, err := qs.k.GetRecordsByBondId(ctx, req.GetId()) + if err != nil { + return nil, err + } + return ®istrytypes.QueryRecordsByBondIdResponse{Records: records}, nil } diff --git a/x/registry/keeper/record_keeper.go b/x/registry/keeper/record_keeper.go index 9dcc8cb5..27cadb8f 100644 --- a/x/registry/keeper/record_keeper.go +++ b/x/registry/keeper/record_keeper.go @@ -17,11 +17,6 @@ type RecordKeeper struct { // storeKey storetypes.StoreKey // Unexposed key to access store from sdk.Context } -// QueryRecordsByBond - get all records for the given bond. -func (k RecordKeeper) QueryRecordsByBond(ctx sdk.Context, bondID string) []registrytypes.Record { - panic("unimplemented") -} - // ProcessRenewRecord renews a record. func (k Keeper) ProcessRenewRecord(ctx sdk.Context, msg registrytypes.MsgRenewRecord) error { panic("unimplemented") diff --git a/x/registry/module/autocli.go b/x/registry/module/autocli.go index 5c420bfd..ba60a563 100644 --- a/x/registry/module/autocli.go +++ b/x/registry/module/autocli.go @@ -35,6 +35,14 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { {ProtoField: "id"}, }, }, + { + RpcMethod: "GetRecordsByBondId", + Use: "get-records-by-bond-id [bond-id]", + Short: "Get records by bond id", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "id"}, + }, + }, }, }, Tx: &autocliv1.ServiceCommandDescriptor{ -- 2.45.2 From 764fb1f04a195fa172bff8aa33743e94883271aa Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Fri, 16 Feb 2024 15:02:52 +0530 Subject: [PATCH 02/13] 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 }, } -- 2.45.2 From 0b8904d7de5c64aed227c7629e4c754d922bea7c Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Mon, 19 Feb 2024 09:32:01 +0530 Subject: [PATCH 03/13] Add a command to get registry module account balances --- x/registry/keeper/keeper.go | 21 ++++++++++++++++++++- x/registry/keeper/query_server.go | 2 ++ x/registry/module/autocli.go | 6 ++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/x/registry/keeper/keeper.go b/x/registry/keeper/keeper.go index f93e1436..fbe65b01 100644 --- a/x/registry/keeper/keeper.go +++ b/x/registry/keeper/keeper.go @@ -326,5 +326,24 @@ func (k Keeper) processAttributeMap(ctx sdk.Context, n ipld.Node, id string, pre // GetModuleBalances gets the registry module account(s) balances. func (k Keeper) GetModuleBalances(ctx sdk.Context) []*registrytypes.AccountBalance { - panic("unimplemented") + var balances []*registrytypes.AccountBalance + accountNames := []string{ + registrytypes.RecordRentModuleAccountName, + registrytypes.AuthorityRentModuleAccountName, + } + + for _, accountName := range accountNames { + moduleAddress := k.accountKeeper.GetModuleAddress(accountName) + + moduleAccount := k.accountKeeper.GetAccount(ctx, moduleAddress) + if moduleAccount != nil { + accountBalance := k.bankKeeper.GetAllBalances(ctx, moduleAddress) + balances = append(balances, ®istrytypes.AccountBalance{ + AccountName: accountName, + Balance: accountBalance, + }) + } + } + + return balances } diff --git a/x/registry/keeper/query_server.go b/x/registry/keeper/query_server.go index 3a3b0e20..f125e196 100644 --- a/x/registry/keeper/query_server.go +++ b/x/registry/keeper/query_server.go @@ -90,7 +90,9 @@ func (qs queryServer) GetRegistryModuleBalance(c context.Context, _ *registrytypes.QueryGetRegistryModuleBalanceRequest, ) (*registrytypes.QueryGetRegistryModuleBalanceResponse, error) { ctx := sdk.UnwrapSDKContext(c) + balances := qs.k.GetModuleBalances(ctx) + return ®istrytypes.QueryGetRegistryModuleBalanceResponse{ Balances: balances, }, nil diff --git a/x/registry/module/autocli.go b/x/registry/module/autocli.go index 343d1f5d..72f7a66e 100644 --- a/x/registry/module/autocli.go +++ b/x/registry/module/autocli.go @@ -51,6 +51,12 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { {ProtoField: "name"}, }, }, + { + RpcMethod: "GetRegistryModuleBalance", + Use: "balance", + Short: "Get registry module account balances", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{}, + }, }, }, Tx: &autocliv1.ServiceCommandDescriptor{ -- 2.45.2 From 5527c91c190ed42d8af0874c03fb134c4fae311d Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Mon, 19 Feb 2024 10:17:52 +0530 Subject: [PATCH 04/13] Add a command to set authority bond id --- x/registry/keeper/msg_server.go | 2 +- x/registry/keeper/naming_keeper.go | 61 ++++++++++++++++++++++++++++-- x/registry/module/autocli.go | 9 +++++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/x/registry/keeper/msg_server.go b/x/registry/keeper/msg_server.go index b2e42cb0..9cd61378 100644 --- a/x/registry/keeper/msg_server.go +++ b/x/registry/keeper/msg_server.go @@ -115,7 +115,7 @@ func (ms msgServer) SetAuthorityBond(c context.Context, msg *registrytypes.MsgSe if err != nil { return nil, err } - err = ms.k.ProcessSetAuthorityBond(ctx, *msg) + err = ms.k.SetAuthorityBond(ctx, *msg) if err != nil { return nil, err } diff --git a/x/registry/keeper/naming_keeper.go b/x/registry/keeper/naming_keeper.go index b2766333..8c2eaba8 100644 --- a/x/registry/keeper/naming_keeper.go +++ b/x/registry/keeper/naming_keeper.go @@ -67,7 +67,7 @@ func (k Keeper) SaveNameAuthority(ctx sdk.Context, name string, authority *regis // updateBlockChangeSetForNameAuthority(ctx, codec, store, name) } -// ProcessReserveAuthority reserves a name authority. +// ReserveAuthority reserves a name authority. func (k Keeper) ReserveAuthority(ctx sdk.Context, msg registrytypes.MsgReserveAuthority) error { crn := fmt.Sprintf("crn://%s", msg.GetName()) parsedCrn, err := url.Parse(crn) @@ -180,8 +180,63 @@ func (k Keeper) createAuthority(ctx sdk.Context, name string, owner string, isRo return nil } -func (k Keeper) ProcessSetAuthorityBond(ctx sdk.Context, msg registrytypes.MsgSetAuthorityBond) error { - panic("unimplemented") +func (k Keeper) SetAuthorityBond(ctx sdk.Context, msg registrytypes.MsgSetAuthorityBond) error { + name := msg.GetName() + signer := msg.GetSigner() + + if has, err := k.HasNameAuthority(ctx, name); !has { + if err != nil { + return err + } + + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Name authority not found.") + } + + authority, err := k.GetNameAuthority(ctx, name) + if err != nil { + return err + } + if authority.OwnerAddress != signer { + return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Access denied.") + } + + if has, err := k.bondKeeper.HasBond(ctx, msg.BondId); !has { + if err != nil { + return err + } + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.") + } + + bond, err := k.bondKeeper.GetBondById(ctx, msg.BondId) + if err != nil { + return err + } + if bond.Owner != signer { + return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond owner mismatch.") + } + + // No-op if bond hasn't changed. + if authority.BondId == msg.BondId { + return nil + } + + // TODO + // Remove old bond ID mapping, if any. + // if authority.BondId != "" { + // k.RemoveBondToAuthorityIndexEntry(ctx, authority.BondId, name) + // } + + // Update bond id and save name authority in store. + authority.BondId = bond.Id + if err = k.SaveNameAuthority(ctx, name, &authority); err != nil { + return err + } + + // TODO + // Add new bond ID mapping. + // k.AddBondToAuthorityIndexEntry(ctx, authority.BondId, name) + + return nil } // ProcessDeleteName removes a CRN -> Record ID mapping. diff --git a/x/registry/module/autocli.go b/x/registry/module/autocli.go index 72f7a66e..44fb09cf 100644 --- a/x/registry/module/autocli.go +++ b/x/registry/module/autocli.go @@ -71,6 +71,15 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { {ProtoField: "owner"}, }, }, + { + RpcMethod: "SetAuthorityBond", + Use: "authority-bond [name] [bond-id]", + Short: "Associate authority with bond", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "name"}, + {ProtoField: "bond_id"}, + }, + }, }, EnhanceCustomCommand: true, // Allow additional manual commands }, -- 2.45.2 From 9ee1f315a1720dd6fb0da84fd9e510c3d126dbd3 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Mon, 19 Feb 2024 18:59:32 +0530 Subject: [PATCH 05/13] Add a command to set name --- x/registry/keeper/keeper.go | 26 +++++ x/registry/keeper/msg_server.go | 5 +- x/registry/keeper/naming_keeper.go | 151 ++++++++++++++++++++++++++++- x/registry/keeper/query_server.go | 2 +- x/registry/keys.go | 3 + x/registry/module/autocli.go | 9 ++ 6 files changed, 189 insertions(+), 7 deletions(-) diff --git a/x/registry/keeper/keeper.go b/x/registry/keeper/keeper.go index fbe65b01..19b1608c 100644 --- a/x/registry/keeper/keeper.go +++ b/x/registry/keeper/keeper.go @@ -61,6 +61,26 @@ func newAuthorityIndexes(sb *collections.SchemaBuilder) AuthoritiesIndexes { return AuthoritiesIndexes{} } +type NameRecordsIndexes struct { + Cid *indexes.Multi[string, string, registrytypes.NameRecord] +} + +func (b NameRecordsIndexes) IndexesList() []collections.Index[string, registrytypes.NameRecord] { + return []collections.Index[string, registrytypes.NameRecord]{b.Cid} +} + +func newNameRecordIndexes(sb *collections.SchemaBuilder) NameRecordsIndexes { + return NameRecordsIndexes{ + Cid: indexes.NewMulti( + sb, registrytypes.NameRecordsByCidIndexPrefix, "name_records_by_cid", + collections.StringKey, collections.StringKey, + func(_ string, v registrytypes.NameRecord) (string, error) { + return v.Latest.Id, nil + }, + ), + } +} + type Keeper struct { cdc codec.BinaryCodec @@ -75,6 +95,7 @@ type Keeper struct { Params collections.Item[registrytypes.Params] Records *collections.IndexedMap[string, registrytypes.Record, RecordsIndexes] Authorities *collections.IndexedMap[string, registrytypes.NameAuthority, AuthoritiesIndexes] + NameRecords *collections.IndexedMap[string, registrytypes.NameRecord, NameRecordsIndexes] } // NewKeeper creates a new Keeper instance @@ -106,6 +127,11 @@ func NewKeeper( collections.StringKey, codec.CollValue[registrytypes.NameAuthority](cdc), newAuthorityIndexes(sb), ), + NameRecords: collections.NewIndexedMap( + sb, registrytypes.NameRecordsPrefix, "name_records", + collections.StringKey, codec.CollValue[registrytypes.NameRecord](cdc), + newNameRecordIndexes(sb), + ), } schema, err := sb.Build() diff --git a/x/registry/keeper/msg_server.go b/x/registry/keeper/msg_server.go index 9cd61378..018d916c 100644 --- a/x/registry/keeper/msg_server.go +++ b/x/registry/keeper/msg_server.go @@ -51,14 +51,17 @@ func (ms msgServer) SetRecord(c context.Context, msg *registrytypes.MsgSetRecord // nolint: all func (ms msgServer) SetName(c context.Context, msg *registrytypes.MsgSetName) (*registrytypes.MsgSetNameResponse, error) { ctx := sdk.UnwrapSDKContext(c) + _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { return nil, err } - err = ms.k.ProcessSetName(ctx, *msg) + + err = ms.k.SetName(ctx, *msg) if err != nil { return nil, err } + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( registrytypes.EventTypeSetRecord, diff --git a/x/registry/keeper/naming_keeper.go b/x/registry/keeper/naming_keeper.go index 8c2eaba8..51e8ce7a 100644 --- a/x/registry/keeper/naming_keeper.go +++ b/x/registry/keeper/naming_keeper.go @@ -45,8 +45,40 @@ func (k Keeper) HasNameRecord(ctx sdk.Context, crn string) bool { } // GetNameRecord - gets a name record from the store. -func (k Keeper) GetNameRecord(ctx sdk.Context, crn string) *registrytypes.NameRecord { - panic("unimplemented") +func (k Keeper) GetNameRecord(ctx sdk.Context, crn string) (*registrytypes.NameRecord, error) { + nameRecord, err := k.NameRecords.Get(ctx, crn) + if err != nil { + if errors.Is(err, collections.ErrNotFound) { + return nil, nil + } + return nil, err + } + + return &nameRecord, nil +} + +// LookupNameRecord - gets a name record which is not stale and under active authority. +func (k Keeper) LookupNameRecord(ctx sdk.Context, crn string) (*registrytypes.NameRecord, error) { + _, _, authority, err := k.getAuthority(ctx, crn) + if err != nil || authority.Status != registrytypes.AuthorityActive { + // If authority is not active (or any other error), lookup fails. + return nil, nil + } + + nameRecord, err := k.GetNameRecord(ctx, crn) + + // Name record may not exist. + if nameRecord == nil { + return nil, err + } + + // Name lookup should fail if the name record is stale. + // i.e. authority was registered later than the name. + if authority.Height > nameRecord.Latest.Height { + return nil, nil + } + + return nameRecord, nil } // ListNameRecords - get all name records. @@ -54,9 +86,56 @@ func (k Keeper) ListNameRecords(ctx sdk.Context) []registrytypes.NameEntry { panic("unimplemented") } -// ProcessSetName creates a CRN -> Record ID mapping. -func (k Keeper) ProcessSetName(ctx sdk.Context, msg registrytypes.MsgSetName) error { - panic("unimplemented") +// SaveNameRecord - sets a name record. +func (k Keeper) SaveNameRecord(ctx sdk.Context, crn string, id string) error { + var nameRecord registrytypes.NameRecord + x, err := k.GetNameRecord(ctx, crn) + if err != nil { + return err + } + + if x != nil { + nameRecord = *x + nameRecord.History = append(nameRecord.History, nameRecord.Latest) + } + + nameRecord.Latest = ®istrytypes.NameRecordEntry{ + Id: id, + Height: uint64(ctx.BlockHeight()), + } + + // TODO: Check if index gets updated on entry updates + if err := k.NameRecords.Set(ctx, crn, nameRecord); err != nil { + return err + } + + // TODO + // Update changeSet for name. + // k.updateBlockChangeSetForName(ctx, crn) + + return nil +} + +// SetName creates a CRN -> Record ID mapping. +func (k Keeper) SetName(ctx sdk.Context, msg registrytypes.MsgSetName) error { + signerAddress, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return err + } + err = k.checkCRNAccess(ctx, signerAddress, msg.Crn) + if err != nil { + return err + } + + nameRecord, err := k.LookupNameRecord(ctx, msg.Crn) + if err != nil { + return err + } + if nameRecord != nil && nameRecord.Latest.Id == msg.Cid { + return nil + } + + return k.SaveNameRecord(ctx, msg.Crn, msg.Cid) } // SaveNameAuthority creates the NameAuthority record. @@ -253,6 +332,68 @@ func (k Keeper) ResolveCRN(ctx sdk.Context, crn string) *registrytypes.Record { panic("unimplemented") } +func (k Keeper) getAuthority(ctx sdk.Context, crn string) (string, *url.URL, *registrytypes.NameAuthority, error) { + parsedCRN, err := url.Parse(crn) + if err != nil { + return "", nil, nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid CRN.") + } + + name := parsedCRN.Host + if has, err := k.HasNameAuthority(ctx, name); !has { + if err != nil { + return "", nil, nil, err + } + + return "", nil, nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Name authority not found.") + } + + authority, err := k.GetNameAuthority(ctx, name) + if err != nil { + return "", nil, nil, err + } + + return name, parsedCRN, &authority, nil +} + +func (k Keeper) checkCRNAccess(ctx sdk.Context, signer sdk.AccAddress, crn string) error { + name, parsedCRN, authority, err := k.getAuthority(ctx, crn) + if err != nil { + return err + } + + formattedCRN := fmt.Sprintf("crn://%s%s", name, parsedCRN.RequestURI()) + if formattedCRN != crn { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid CRN.") + } + + if authority.OwnerAddress != signer.String() { + return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Access denied.") + } + + if authority.Status != registrytypes.AuthorityActive { + return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Authority is not active.") + } + + if authority.BondId == "" || len(authority.BondId) == 0 { + return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Authority bond not found.") + } + + if authority.OwnerPublicKey == "" { + // Try to set owner public key if account has it available now. + ownerAccount := k.accountKeeper.GetAccount(ctx, signer) + pubKey := ownerAccount.GetPubKey() + if pubKey != nil { + // Update public key in authority record. + authority.OwnerPublicKey = getAuthorityPubKey(pubKey) + if err = k.SaveNameAuthority(ctx, name, authority); err != nil { + return err + } + } + } + + return nil +} + func getAuthorityPubKey(pubKey cryptotypes.PubKey) string { if pubKey == nil { return "" diff --git a/x/registry/keeper/query_server.go b/x/registry/keeper/query_server.go index f125e196..ab6999d4 100644 --- a/x/registry/keeper/query_server.go +++ b/x/registry/keeper/query_server.go @@ -121,7 +121,7 @@ func (qs queryServer) LookupCrn(c context.Context, req *registrytypes.QueryLooku if !qs.k.HasNameRecord(ctx, crn) { return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "CRN not found.") } - nameRecord := qs.k.GetNameRecord(ctx, crn) + nameRecord, _ := qs.k.LookupNameRecord(ctx, crn) if nameRecord == nil { return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "name record not found.") } diff --git a/x/registry/keys.go b/x/registry/keys.go index 0f6c57cd..4063e74a 100644 --- a/x/registry/keys.go +++ b/x/registry/keys.go @@ -24,4 +24,7 @@ var ( AuthoritiesPrefix = collections.NewPrefix(3) AuthoritiesByAuctionIdIndexPrefix = collections.NewPrefix(4) AuthoritiesByBondIdIndexPrefix = collections.NewPrefix(5) + + NameRecordsPrefix = collections.NewPrefix(6) + NameRecordsByCidIndexPrefix = collections.NewPrefix(7) ) diff --git a/x/registry/module/autocli.go b/x/registry/module/autocli.go index 44fb09cf..177ed29d 100644 --- a/x/registry/module/autocli.go +++ b/x/registry/module/autocli.go @@ -80,6 +80,15 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { {ProtoField: "bond_id"}, }, }, + { + RpcMethod: "SetName", + Use: "set-name [crn] [cid]", + Short: "Set CRN to CID mapping", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "crn"}, + {ProtoField: "cid"}, + }, + }, }, EnhanceCustomCommand: true, // Allow additional manual commands }, -- 2.45.2 From 5b116308eb269398a49c7e8ebbe7ade2861aa3ea Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Mon, 19 Feb 2024 19:11:03 +0530 Subject: [PATCH 06/13] Add a command to get naming info --- x/registry/keeper/naming_keeper.go | 4 ++-- x/registry/keeper/query_server.go | 9 ++++++++- x/registry/module/autocli.go | 8 ++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/x/registry/keeper/naming_keeper.go b/x/registry/keeper/naming_keeper.go index 51e8ce7a..1f2e032c 100644 --- a/x/registry/keeper/naming_keeper.go +++ b/x/registry/keeper/naming_keeper.go @@ -40,8 +40,8 @@ func (k Keeper) GetNameAuthority(ctx sdk.Context, name string) (registrytypes.Na } // HasNameRecord - checks if a name record exists. -func (k Keeper) HasNameRecord(ctx sdk.Context, crn string) bool { - panic("unimplemented") +func (k Keeper) HasNameRecord(ctx sdk.Context, crn string) (bool, error) { + return k.NameRecords.Has(ctx, crn) } // GetNameRecord - gets a name record from the store. diff --git a/x/registry/keeper/query_server.go b/x/registry/keeper/query_server.go index ab6999d4..c242fafd 100644 --- a/x/registry/keeper/query_server.go +++ b/x/registry/keeper/query_server.go @@ -118,13 +118,20 @@ func (qs queryServer) Whois(c context.Context, request *registrytypes.QueryWhois func (qs queryServer) LookupCrn(c context.Context, req *registrytypes.QueryLookupCrnRequest) (*registrytypes.QueryLookupCrnResponse, error) { ctx := sdk.UnwrapSDKContext(c) crn := req.GetCrn() - if !qs.k.HasNameRecord(ctx, crn) { + + crnExists, err := qs.k.HasNameRecord(ctx, crn) + if err != nil { + return nil, err + } + if !crnExists { return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "CRN not found.") } + nameRecord, _ := qs.k.LookupNameRecord(ctx, crn) if nameRecord == nil { return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "name record not found.") } + return ®istrytypes.QueryLookupCrnResponse{Name: nameRecord}, nil } diff --git a/x/registry/module/autocli.go b/x/registry/module/autocli.go index 177ed29d..1419565f 100644 --- a/x/registry/module/autocli.go +++ b/x/registry/module/autocli.go @@ -51,6 +51,14 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { {ProtoField: "name"}, }, }, + { + RpcMethod: "LookupCrn", + Use: "lookup [crn]", + Short: "Get naming info for CRN", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "crn"}, + }, + }, { RpcMethod: "GetRegistryModuleBalance", Use: "balance", -- 2.45.2 From c809f22b83e1e0f8d5b971d7ebedba8a2272bf8d Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Mon, 19 Feb 2024 19:23:31 +0530 Subject: [PATCH 07/13] Add a command to list name records --- x/registry/keeper/naming_keeper.go | 15 +++++++++++++-- x/registry/keeper/query_server.go | 7 ++++++- x/registry/module/autocli.go | 6 ++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/x/registry/keeper/naming_keeper.go b/x/registry/keeper/naming_keeper.go index 1f2e032c..2a19f3af 100644 --- a/x/registry/keeper/naming_keeper.go +++ b/x/registry/keeper/naming_keeper.go @@ -82,8 +82,19 @@ func (k Keeper) LookupNameRecord(ctx sdk.Context, crn string) (*registrytypes.Na } // ListNameRecords - get all name records. -func (k Keeper) ListNameRecords(ctx sdk.Context) []registrytypes.NameEntry { - panic("unimplemented") +func (k Keeper) ListNameRecords(ctx sdk.Context) ([]registrytypes.NameEntry, error) { + var nameEntries []registrytypes.NameEntry + + err := k.NameRecords.Walk(ctx, nil, func(key string, value registrytypes.NameRecord) (stop bool, err error) { + nameEntries = append(nameEntries, registrytypes.NameEntry{ + Name: key, + Entry: &value, + }) + + return false, nil + }) + + return nameEntries, err } // SaveNameRecord - sets a name record. diff --git a/x/registry/keeper/query_server.go b/x/registry/keeper/query_server.go index c242fafd..1f935031 100644 --- a/x/registry/keeper/query_server.go +++ b/x/registry/keeper/query_server.go @@ -100,7 +100,12 @@ func (qs queryServer) GetRegistryModuleBalance(c context.Context, func (qs queryServer) NameRecords(c context.Context, _ *registrytypes.QueryNameRecordsRequest) (*registrytypes.QueryNameRecordsResponse, error) { ctx := sdk.UnwrapSDKContext(c) - nameRecords := qs.k.ListNameRecords(ctx) + + nameRecords, err := qs.k.ListNameRecords(ctx) + if err != nil { + return nil, err + } + return ®istrytypes.QueryNameRecordsResponse{Names: nameRecords}, nil } diff --git a/x/registry/module/autocli.go b/x/registry/module/autocli.go index 1419565f..45b76af1 100644 --- a/x/registry/module/autocli.go +++ b/x/registry/module/autocli.go @@ -51,6 +51,12 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { {ProtoField: "name"}, }, }, + { + RpcMethod: "NameRecords", + Use: "names", + Short: "List name records", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{}, + }, { RpcMethod: "LookupCrn", Use: "lookup [crn]", -- 2.45.2 From 22f6e05de06901680d026cf7248bbe49a3b1d289 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Tue, 20 Feb 2024 10:11:01 +0530 Subject: [PATCH 08/13] Add a command to resolve name to a record --- x/registry/keeper/naming_keeper.go | 53 +++++++++++++++++++++++++++--- x/registry/keeper/query_server.go | 14 ++++++-- x/registry/module/autocli.go | 8 +++++ 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/x/registry/keeper/naming_keeper.go b/x/registry/keeper/naming_keeper.go index 2a19f3af..4634a8c6 100644 --- a/x/registry/keeper/naming_keeper.go +++ b/x/registry/keeper/naming_keeper.go @@ -100,13 +100,13 @@ func (k Keeper) ListNameRecords(ctx sdk.Context) ([]registrytypes.NameEntry, err // SaveNameRecord - sets a name record. func (k Keeper) SaveNameRecord(ctx sdk.Context, crn string, id string) error { var nameRecord registrytypes.NameRecord - x, err := k.GetNameRecord(ctx, crn) + existingNameRecord, err := k.GetNameRecord(ctx, crn) if err != nil { return err } - if x != nil { - nameRecord = *x + if existingNameRecord != nil { + nameRecord = *existingNameRecord nameRecord.History = append(nameRecord.History, nameRecord.Latest) } @@ -339,8 +339,51 @@ func (k Keeper) GetAuthorityExpiryQueue(ctx sdk.Context) []*registrytypes.Expiry } // ResolveCRN resolves a CRN to a record. -func (k Keeper) ResolveCRN(ctx sdk.Context, crn string) *registrytypes.Record { - panic("unimplemented") +func (k Keeper) ResolveCRN(ctx sdk.Context, crn string) (*registrytypes.Record, error) { + _, _, authority, err := k.getAuthority(ctx, crn) + if err != nil || authority.Status != registrytypes.AuthorityActive { + // If authority is not active (or any other error), resolution fails. + return nil, err + } + + // Name should not resolve if it's stale. + // i.e. authority was registered later than the name. + record, nameRecord, err := k.resolveCRNRecord(ctx, crn) + if err != nil { + return nil, err + } + if authority.Height > nameRecord.Latest.Height { + return nil, nil + } + + return record, nil +} + +func (k Keeper) resolveCRNRecord(ctx sdk.Context, crn string) (*registrytypes.Record, *registrytypes.NameRecord, error) { + nameRecord, err := k.GetNameRecord(ctx, crn) + if nameRecord == nil { + return nil, nil, err + } + + latestRecordId := nameRecord.Latest.Id + if latestRecordId == "" { + return nil, nameRecord, nil + } + + if has, err := k.HasRecord(ctx, latestRecordId); !has { + if err != nil { + return nil, nil, err + } + + return nil, nameRecord, nil + } + + record, err := k.GetRecordById(ctx, latestRecordId) + if err != nil { + return nil, nil, err + } + + return &record, nameRecord, nil } func (k Keeper) getAuthority(ctx sdk.Context, crn string) (string, *url.URL, *registrytypes.NameAuthority, error) { diff --git a/x/registry/keeper/query_server.go b/x/registry/keeper/query_server.go index 1f935031..b344e481 100644 --- a/x/registry/keeper/query_server.go +++ b/x/registry/keeper/query_server.go @@ -132,8 +132,12 @@ func (qs queryServer) LookupCrn(c context.Context, req *registrytypes.QueryLooku return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "CRN not found.") } - nameRecord, _ := qs.k.LookupNameRecord(ctx, crn) + nameRecord, err := qs.k.LookupNameRecord(ctx, crn) if nameRecord == nil { + if err != nil { + return nil, err + } + return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "name record not found.") } @@ -142,11 +146,17 @@ func (qs queryServer) LookupCrn(c context.Context, req *registrytypes.QueryLooku func (qs queryServer) ResolveCrn(c context.Context, req *registrytypes.QueryResolveCrnRequest) (*registrytypes.QueryResolveCrnResponse, error) { ctx := sdk.UnwrapSDKContext(c) + crn := req.GetCrn() - record := qs.k.ResolveCRN(ctx, crn) + record, err := qs.k.ResolveCRN(ctx, crn) if record == nil { + if err != nil { + return nil, err + } + return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "record not found.") } + return ®istrytypes.QueryResolveCrnResponse{Record: record}, nil } diff --git a/x/registry/module/autocli.go b/x/registry/module/autocli.go index 45b76af1..c9115569 100644 --- a/x/registry/module/autocli.go +++ b/x/registry/module/autocli.go @@ -65,6 +65,14 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { {ProtoField: "crn"}, }, }, + { + RpcMethod: "ResolveCrn", + Use: "resolve [crn]", + Short: "Resolve CRN to record", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "crn"}, + }, + }, { RpcMethod: "GetRegistryModuleBalance", Use: "balance", -- 2.45.2 From 75737915ab2f2e80e77c8f1503f43a9cddda5081 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 22 Feb 2024 14:34:25 +0530 Subject: [PATCH 09/13] Add a command to delete a name --- x/registry/keeper/msg_server.go | 6 +++++- x/registry/keeper/naming_keeper.go | 24 +++++++++++++++++++++--- x/registry/module/autocli.go | 8 ++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/x/registry/keeper/msg_server.go b/x/registry/keeper/msg_server.go index 018d916c..4152d62f 100644 --- a/x/registry/keeper/msg_server.go +++ b/x/registry/keeper/msg_server.go @@ -21,6 +21,7 @@ func NewMsgServerImpl(keeper Keeper) registrytypes.MsgServer { func (ms msgServer) SetRecord(c context.Context, msg *registrytypes.MsgSetRecord) (*registrytypes.MsgSetRecordResponse, error) { ctx := sdk.UnwrapSDKContext(c) + _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { return nil, err @@ -114,6 +115,7 @@ func (ms msgServer) ReserveName(c context.Context, msg *registrytypes.MsgReserve // nolint: all func (ms msgServer) SetAuthorityBond(c context.Context, msg *registrytypes.MsgSetAuthorityBond) (*registrytypes.MsgSetAuthorityBondResponse, error) { ctx := sdk.UnwrapSDKContext(c) + _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { return nil, err @@ -140,14 +142,16 @@ func (ms msgServer) SetAuthorityBond(c context.Context, msg *registrytypes.MsgSe func (ms msgServer) DeleteName(c context.Context, msg *registrytypes.MsgDeleteNameAuthority) (*registrytypes.MsgDeleteNameAuthorityResponse, error) { ctx := sdk.UnwrapSDKContext(c) + _, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { return nil, err } - err = ms.k.ProcessDeleteName(ctx, *msg) + err = ms.k.DeleteName(ctx, *msg) if err != nil { return nil, err } + ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( registrytypes.EventTypeDeleteName, diff --git a/x/registry/keeper/naming_keeper.go b/x/registry/keeper/naming_keeper.go index 4634a8c6..173a4788 100644 --- a/x/registry/keeper/naming_keeper.go +++ b/x/registry/keeper/naming_keeper.go @@ -329,9 +329,27 @@ func (k Keeper) SetAuthorityBond(ctx sdk.Context, msg registrytypes.MsgSetAuthor return nil } -// ProcessDeleteName removes a CRN -> Record ID mapping. -func (k Keeper) ProcessDeleteName(ctx sdk.Context, msg registrytypes.MsgDeleteNameAuthority) error { - panic("unimplemented") +// DeleteName removes a CRN -> Record ID mapping. +func (k Keeper) DeleteName(ctx sdk.Context, msg registrytypes.MsgDeleteNameAuthority) error { + signerAddress, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return err + } + err = k.checkCRNAccess(ctx, signerAddress, msg.Crn) + if err != nil { + return err + } + + crnExists, err := k.HasNameRecord(ctx, msg.Crn) + if err != nil { + return err + } + if !crnExists { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Name not found.") + } + + // Set CID to empty string. + return k.SaveNameRecord(ctx, msg.Crn, "") } func (k Keeper) GetAuthorityExpiryQueue(ctx sdk.Context) []*registrytypes.ExpiryQueueRecord { diff --git a/x/registry/module/autocli.go b/x/registry/module/autocli.go index c9115569..e08271ec 100644 --- a/x/registry/module/autocli.go +++ b/x/registry/module/autocli.go @@ -111,6 +111,14 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { {ProtoField: "cid"}, }, }, + { + RpcMethod: "DeleteName", + Use: "delete-name [crn]", + Short: "Delete CRN", + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "crn"}, + }, + }, }, EnhanceCustomCommand: true, // Allow additional manual commands }, -- 2.45.2 From 00d821538fc7d9270642b54431e2c2d72da338a2 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 22 Feb 2024 15:09:40 +0530 Subject: [PATCH 10/13] Implement genesis import and export keepers --- x/registry/keeper/genesis.go | 111 +++++++++++++++++------------ x/registry/keeper/naming_keeper.go | 15 ++++ 2 files changed, 80 insertions(+), 46 deletions(-) diff --git a/x/registry/keeper/genesis.go b/x/registry/keeper/genesis.go index 7621e309..cd6b9856 100644 --- a/x/registry/keeper/genesis.go +++ b/x/registry/keeper/genesis.go @@ -1,80 +1,99 @@ package keeper import ( - "context" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" "git.vdb.to/cerc-io/laconic2d/x/registry" ) // InitGenesis initializes the module state from a genesis state. -func (k *Keeper) InitGenesis(ctx context.Context, data *registry.GenesisState) error { +func (k *Keeper) InitGenesis(ctx sdk.Context, data *registry.GenesisState) error { if err := k.Params.Set(ctx, data.Params); err != nil { return err } - // for _, record := range data.Records { - // keeper.PutRecord(ctx, record) + for _, record := range data.Records { + if err := k.SaveRecord(ctx, record); err != nil { + return err + } - // // Add to record expiry queue if expiry time is in the future. - // expiryTime, err := time.Parse(time.RFC3339, record.ExpiryTime) - // if err != nil { - // panic(err) - // } + // Add to record expiry queue if expiry time is in the future. + expiryTime, err := time.Parse(time.RFC3339, record.ExpiryTime) + if err != nil { + panic(err) + } - // if expiryTime.After(ctx.BlockTime()) { - // keeper.InsertRecordExpiryQueue(ctx, record) - // } + if expiryTime.After(ctx.BlockTime()) { + // TODO + // k.InsertRecordExpiryQueue(ctx, record) + } + } - // // Note: Bond genesis runs first, so bonds will already be present. - // if record.BondId != "" { - // keeper.AddBondToRecordIndexEntry(ctx, record.BondId, record.Id) - // } - // } + for _, authority := range data.Authorities { + // Only import authorities that are marked active. + if authority.Entry.Status == registry.AuthorityActive { + if err := k.SaveNameAuthority(ctx, authority.Name, authority.Entry); err != nil { + return err + } - // for _, authority := range data.Authorities { - // // Only import authorities that are marked active. - // if authority.Entry.Status == types.AuthorityActive { - // keeper.SetNameAuthority(ctx, authority.Name, authority.Entry) + // TODO + // Add authority name to expiry queue. + // k.InsertAuthorityExpiryQueue(ctx, authority.Name, authority.Entry.ExpiryTime) - // // Add authority name to expiry queue. - // keeper.InsertAuthorityExpiryQueue(ctx, authority.Name, authority.Entry.ExpiryTime) + // TODO + // Note: Bond genesis runs first, so bonds will already be present. + // if authority.Entry.BondId != "" { + // k.AddBondToAuthorityIndexEntry(ctx, authority.Entry.BondId, authority.Name) + // } + } + } - // // Note: Bond genesis runs first, so bonds will already be present. - // if authority.Entry.BondId != "" { - // keeper.AddBondToAuthorityIndexEntry(ctx, authority.Entry.BondId, authority.Name) - // } - // } - // } - - // for _, nameEntry := range data.Names { - // keeper.SetNameRecord(ctx, nameEntry.Name, nameEntry.Entry.Latest.Id) - // } + for _, nameEntry := range data.Names { + if err := k.SaveNameRecord(ctx, nameEntry.Name, nameEntry.Entry.Latest.Id); err != nil { + return err + } + } return nil } // ExportGenesis exports the module state to a genesis state. -func (k *Keeper) ExportGenesis(ctx context.Context) (*registry.GenesisState, error) { +func (k *Keeper) ExportGenesis(ctx sdk.Context) (*registry.GenesisState, error) { params, err := k.Params.Get(ctx) if err != nil { return nil, err } - // records := keeper.ListRecords(ctx) + records, err := k.ListRecords(ctx) + if err != nil { + return nil, err + } - // authorities := keeper.ListNameAuthorityRecords(ctx) - // authorityEntries := []types.AuthorityEntry{} - // // #nosec G705 - // for name, record := range authorities { - // authorityEntries = append(authorityEntries, types.AuthorityEntry{ - // Name: name, - // Entry: &record, //nolint: all - // }) // #nosec G601 - // } + authorities, err := k.ListNameAuthorityRecords(ctx) + if err != nil { + return nil, err + } - // names := keeper.ListNameRecords(ctx) + authorityEntries := []registry.AuthorityEntry{} + // #nosec G705 + for name, record := range authorities { + authorityEntries = append(authorityEntries, registry.AuthorityEntry{ + Name: name, + Entry: &record, //nolint: all + }) // #nosec G601 + } + + names, err := k.ListNameRecords(ctx) + if err != nil { + return nil, err + } return ®istry.GenesisState{ - Params: params, + Params: params, + Records: records, + Authorities: authorityEntries, + Names: names, }, nil } diff --git a/x/registry/keeper/naming_keeper.go b/x/registry/keeper/naming_keeper.go index 173a4788..fe829a80 100644 --- a/x/registry/keeper/naming_keeper.go +++ b/x/registry/keeper/naming_keeper.go @@ -39,6 +39,21 @@ func (k Keeper) GetNameAuthority(ctx sdk.Context, name string) (registrytypes.Na return authority, nil } +// ListNameAuthorityRecords - get all name authority records. +func (k Keeper) ListNameAuthorityRecords(ctx sdk.Context) (map[string]registrytypes.NameAuthority, error) { + nameAuthorityRecords := make(map[string]registrytypes.NameAuthority) + + err := k.Authorities.Walk(ctx, nil, func(key string, value registrytypes.NameAuthority) (bool, error) { + nameAuthorityRecords[key] = value + return false, nil + }) + if err != nil { + return map[string]registrytypes.NameAuthority{}, err + } + + return nameAuthorityRecords, nil +} + // HasNameRecord - checks if a name record exists. func (k Keeper) HasNameRecord(ctx sdk.Context, crn string) (bool, error) { return k.NameRecords.Has(ctx, crn) -- 2.45.2 From 0ac8398979e665296dea3b9bfd45c0befe203bf9 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 22 Feb 2024 16:28:27 +0530 Subject: [PATCH 11/13] Populate names when fetching records --- x/registry/keeper/keeper.go | 53 ++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/x/registry/keeper/keeper.go b/x/registry/keeper/keeper.go index 19b1608c..688ae0ec 100644 --- a/x/registry/keeper/keeper.go +++ b/x/registry/keeper/keeper.go @@ -156,15 +156,21 @@ func (k Keeper) HasRecord(ctx sdk.Context, id string) (bool, error) { // ListRecords - get all records. func (k Keeper) ListRecords(ctx sdk.Context) ([]registrytypes.Record, error) { - iter, err := k.Records.Iterate(ctx, nil) + var records []registrytypes.Record + + err := k.Records.Walk(ctx, nil, func(key string, value registrytypes.Record) (bool, error) { + if err := k.populateRecordNames(ctx, &value); err != nil { + return true, err + } + records = append(records, value) + + return false, nil + }) if err != nil { return nil, err } - // TODO: Check if required - // decodeRecordNames(store, &record) - - return iter.Values() + return records, nil } // GetRecordById - gets a record from the store. @@ -174,17 +180,35 @@ func (k Keeper) GetRecordById(ctx sdk.Context, id string) (registrytypes.Record, return registrytypes.Record{}, err } + if err := k.populateRecordNames(ctx, &record); err != nil { + return registrytypes.Record{}, err + } + return record, nil } // GetRecordsByBondId - gets a record from the store. func (k Keeper) GetRecordsByBondId(ctx sdk.Context, bondId string) ([]registrytypes.Record, error) { - iter, err := k.Records.Indexes.BondId.MatchExact(ctx, bondId) + var records []registrytypes.Record + + err := k.Records.Indexes.BondId.Walk(ctx, collections.NewPrefixedPairRange[string, string](bondId), func(bondId string, id string) (bool, error) { + record, err := k.Records.Get(ctx, id) + if err != nil { + return true, err + } + + if err := k.populateRecordNames(ctx, &record); err != nil { + return true, err + } + records = append(records, record) + + return false, nil + }) if err != nil { return []registrytypes.Record{}, err } - return indexes.CollectValues(ctx, k.Records, iter) + return records, nil } // RecordsFromAttributes gets a list of records whose attributes match all provided values @@ -350,6 +374,21 @@ func (k Keeper) processAttributeMap(ctx sdk.Context, n ipld.Node, id string, pre return nil } +func (k Keeper) populateRecordNames(ctx sdk.Context, record *registrytypes.Record) error { + iter, err := k.NameRecords.Indexes.Cid.MatchExact(ctx, record.Id) + if err != nil { + return err + } + + names, err := iter.PrimaryKeys() + if err != nil { + return err + } + record.Names = names + + return nil +} + // GetModuleBalances gets the registry module account(s) balances. func (k Keeper) GetModuleBalances(ctx sdk.Context) []*registrytypes.AccountBalance { var balances []*registrytypes.AccountBalance -- 2.45.2 From 332fa8da37c6d4cb12cc170f452ae21d28197de1 Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 22 Feb 2024 16:49:57 +0530 Subject: [PATCH 12/13] Handle reservation for sub authority --- x/registry/keeper/naming_keeper.go | 47 +++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/x/registry/keeper/naming_keeper.go b/x/registry/keeper/naming_keeper.go index fe829a80..a0433e5f 100644 --- a/x/registry/keeper/naming_keeper.go +++ b/x/registry/keeper/naming_keeper.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net/url" + "strings" "cosmossdk.io/collections" errorsmod "cosmossdk.io/errors" @@ -185,10 +186,9 @@ func (k Keeper) ReserveAuthority(ctx sdk.Context, msg registrytypes.MsgReserveAu return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid name") } - // TODO - // if strings.Contains(name, ".") { - // return k.ProcessReserveSubAuthority(ctx, name, msg) - // } + if strings.Contains(name, ".") { + return k.ReserveSubAuthority(ctx, name, msg) + } err = k.createAuthority(ctx, name, msg.GetSigner(), true) if err != nil { @@ -198,6 +198,45 @@ func (k Keeper) ReserveAuthority(ctx sdk.Context, msg registrytypes.MsgReserveAu return nil } +// ReserveSubAuthority reserves a sub-authority. +func (k Keeper) ReserveSubAuthority(ctx sdk.Context, name string, msg registrytypes.MsgReserveAuthority) error { + // Get parent authority name. + names := strings.Split(name, ".") + parent := strings.Join(names[1:], ".") + + // Check if parent authority exists. + if has, err := k.HasNameAuthority(ctx, parent); !has { + if err != nil { + return err + } + + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Parent authority not found.") + } + parentAuthority, err := k.GetNameAuthority(ctx, parent) + if err != nil { + return err + } + + // Sub-authority creator needs to be the owner of the parent authority. + if parentAuthority.OwnerAddress != msg.Signer { + return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Access denied.") + } + + // Sub-authority owner defaults to parent authority owner. + subAuthorityOwner := msg.Signer + if len(msg.Owner) != 0 { + // Override sub-authority owner if provided in message. + subAuthorityOwner = msg.Owner + } + + sdkErr := k.createAuthority(ctx, name, subAuthorityOwner, false) + if sdkErr != nil { + return sdkErr + } + + return nil +} + func (k Keeper) createAuthority(ctx sdk.Context, name string, owner string, isRoot bool) error { moduleParams, err := k.GetParams(ctx) if err != nil { -- 2.45.2 From 30a88790f2fab9a104c5d1c72a8cd31a15d2ca0c Mon Sep 17 00:00:00 2001 From: Prathamesh Musale Date: Thu, 22 Feb 2024 18:32:09 +0530 Subject: [PATCH 13/13] Fix auctions iteration --- x/auction/keeper/keeper.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/x/auction/keeper/keeper.go b/x/auction/keeper/keeper.go index 578fc361..e9ae55fd 100644 --- a/x/auction/keeper/keeper.go +++ b/x/auction/keeper/keeper.go @@ -217,17 +217,17 @@ func (k Keeper) ListAuctions(ctx sdk.Context) ([]auctiontypes.Auction, error) { func (k Keeper) MatchAuctions(ctx sdk.Context, matchFn func(*auctiontypes.Auction) (bool, error)) ([]*auctiontypes.Auction, error) { var auctions []*auctiontypes.Auction - err := k.Auctions.Walk(ctx, nil, func(key string, value auctiontypes.Auction) (stop bool, err error) { + err := k.Auctions.Walk(ctx, nil, func(key string, value auctiontypes.Auction) (bool, error) { auctionMatched, err := matchFn(&value) if err != nil { - return false, err + return true, err } if auctionMatched { auctions = append(auctions, &value) } - return true, nil + return false, nil }) if err != nil { return nil, err @@ -532,10 +532,8 @@ func (k Keeper) GetAuctionModuleBalances(ctx sdk.Context) sdk.Coins { } func (k Keeper) EndBlockerProcessAuctions(ctx sdk.Context) error { - var err error - // Transition auction state (commit, reveal, expired, completed). - if err = k.processAuctionPhases(ctx); err != nil { + if err := k.processAuctionPhases(ctx); err != nil { return err } -- 2.45.2