Add a command to set record with keeper placeholders
This commit is contained in:
parent
de489a4d21
commit
538010f377
1
go.mod
1
go.mod
@ -29,6 +29,7 @@ require (
|
|||||||
github.com/cosmos/cosmos-sdk v0.50.3
|
github.com/cosmos/cosmos-sdk v0.50.3
|
||||||
github.com/cosmos/go-bip39 v1.0.0
|
github.com/cosmos/go-bip39 v1.0.0
|
||||||
github.com/cosmos/gogoproto v1.4.11
|
github.com/cosmos/gogoproto v1.4.11
|
||||||
|
github.com/deckarep/golang-set v1.8.0
|
||||||
github.com/gibson042/canonicaljson-go v1.0.3
|
github.com/gibson042/canonicaljson-go v1.0.3
|
||||||
github.com/golang/protobuf v1.5.3
|
github.com/golang/protobuf v1.5.3
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0
|
||||||
|
2
go.sum
2
go.sum
@ -213,6 +213,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
|
||||||
|
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||||
|
@ -360,3 +360,38 @@ func (k Keeper) getMaxBondAmount(ctx sdk.Context) (sdk.Coins, error) {
|
|||||||
maxBondAmount := params.MaxBondAmount
|
maxBondAmount := params.MaxBondAmount
|
||||||
return sdk.NewCoins(maxBondAmount), nil
|
return sdk.NewCoins(maxBondAmount), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransferCoinsToModuleAccount moves funds from the bonds module account to another module account.
|
||||||
|
func (k Keeper) TransferCoinsToModuleAccount(ctx sdk.Context, id, moduleAccount string, coins sdk.Coins) error {
|
||||||
|
if has, err := k.HasBond(ctx, id); !has {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Bond not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
bond, err := k.GetBondById(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduct rent from bond.
|
||||||
|
updatedBalance, isNeg := bond.Balance.SafeSub(coins...)
|
||||||
|
|
||||||
|
if isNeg {
|
||||||
|
// Check if bond has sufficient funds.
|
||||||
|
return errorsmod.Wrap(sdkerrors.ErrInsufficientFunds, "Insufficient funds.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move funds from bond module to record rent module.
|
||||||
|
err = k.bankKeeper.SendCoinsFromModuleToModule(ctx, bondtypes.ModuleName, moduleAccount, coins)
|
||||||
|
if err != nil {
|
||||||
|
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Error transferring funds.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update bond balance.
|
||||||
|
bond.Balance = updatedBalance
|
||||||
|
err = k.SaveBond(ctx, &bond)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -10,12 +10,12 @@ import (
|
|||||||
bondtypes "git.vdb.to/cerc-io/laconic2d/x/bond"
|
bondtypes "git.vdb.to/cerc-io/laconic2d/x/bond"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ bondtypes.QueryServer = queryServer{}
|
||||||
|
|
||||||
type queryServer struct {
|
type queryServer struct {
|
||||||
k Keeper
|
k Keeper
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ bondtypes.QueryServer = queryServer{}
|
|
||||||
|
|
||||||
// NewQueryServerImpl returns an implementation of the module QueryServer.
|
// NewQueryServerImpl returns an implementation of the module QueryServer.
|
||||||
func NewQueryServerImpl(k Keeper) bondtypes.QueryServer {
|
func NewQueryServerImpl(k Keeper) bondtypes.QueryServer {
|
||||||
return queryServer{k}
|
return queryServer{k}
|
||||||
|
91
x/registry/client/cli/tx.go
Normal file
91
x/registry/client/cli/tx.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetTxCmd returns transaction commands for this module.
|
||||||
|
func GetTxCmd() *cobra.Command {
|
||||||
|
registryTxCmd := &cobra.Command{
|
||||||
|
Use: registrytypes.ModuleName,
|
||||||
|
Short: "registry transaction subcommands",
|
||||||
|
DisableFlagParsing: true,
|
||||||
|
SuggestionsMinimumDistance: 2,
|
||||||
|
RunE: client.ValidateCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
registryTxCmd.AddCommand(
|
||||||
|
GetCmdSetRecord(),
|
||||||
|
// GetCmdRenewRecord(),
|
||||||
|
// GetCmdAssociateBond(),
|
||||||
|
// GetCmdDissociateBond(),
|
||||||
|
// GetCmdDissociateRecords(),
|
||||||
|
// GetCmdReAssociateRecords(),
|
||||||
|
// GetCmdSetName(),
|
||||||
|
// GetCmdReserveName(),
|
||||||
|
// GetCmdSetAuthorityBond(),
|
||||||
|
// GetCmdDeleteName(),
|
||||||
|
)
|
||||||
|
|
||||||
|
return registryTxCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCmdSetRecord is the CLI command for creating/updating a record.
|
||||||
|
func GetCmdSetRecord() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "set [payload-file-path] [bond-id]",
|
||||||
|
Short: "Set record",
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
clientCtx, err := client.GetClientTxContext(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadType, err := GetPayloadFromFile(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := payloadType.ToPayload()
|
||||||
|
|
||||||
|
msg := registrytypes.NewMsgSetRecord(payload, args[1], clientCtx.GetFromAddress())
|
||||||
|
err = msg.ValidateBasic()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
flags.AddTxFlagsToCmd(cmd)
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPayloadFromFile loads payload object from YAML file.
|
||||||
|
func GetPayloadFromFile(filePath string) (*registrytypes.ReadablePayload, error) {
|
||||||
|
var payload registrytypes.ReadablePayload
|
||||||
|
|
||||||
|
data, err := os.ReadFile(filePath) // #nosec G304
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(data, &payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &payload, nil
|
||||||
|
}
|
@ -7,17 +7,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func RegisterInterfaces(registry types.InterfaceRegistry) {
|
func RegisterInterfaces(registry types.InterfaceRegistry) {
|
||||||
registry.RegisterImplementations((*sdk.Msg)(nil)) // &MsgSetName{},
|
registry.RegisterImplementations((*sdk.Msg)(nil),
|
||||||
// &MsgReserveAuthority{},
|
&MsgSetName{},
|
||||||
// &MsgDeleteNameAuthority{},
|
&MsgReserveAuthority{},
|
||||||
// &MsgSetAuthorityBond{},
|
&MsgDeleteNameAuthority{},
|
||||||
|
&MsgSetAuthorityBond{},
|
||||||
|
|
||||||
// &MsgSetRecord{},
|
&MsgSetRecord{},
|
||||||
// &MsgRenewRecord{},
|
&MsgRenewRecord{},
|
||||||
// &MsgAssociateBond{},
|
&MsgAssociateBond{},
|
||||||
// &MsgDissociateBond{},
|
&MsgDissociateBond{},
|
||||||
// &MsgDissociateRecords{},
|
&MsgDissociateRecords{},
|
||||||
// &MsgReAssociateRecords{},
|
&MsgReAssociateRecords{},
|
||||||
|
)
|
||||||
|
|
||||||
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
|
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
|
||||||
}
|
}
|
||||||
|
25
x/registry/events.go
Normal file
25
x/registry/events.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
const (
|
||||||
|
EventTypeSetRecord = "set"
|
||||||
|
EventTypeDeleteName = "delete-name"
|
||||||
|
EventTypeReserveNameAuthority = "reserve-authority"
|
||||||
|
EventTypeAuthorityBond = "authority-bond"
|
||||||
|
EventTypeRenewRecord = "renew-record"
|
||||||
|
EventTypeAssociateBond = "associate-bond"
|
||||||
|
EventTypeDissociateBond = "dissociate-bond"
|
||||||
|
EventTypeDissociateRecords = "dissociate-record"
|
||||||
|
EventTypeReAssociateRecords = "re-associate-records"
|
||||||
|
|
||||||
|
AttributeKeySigner = "signer"
|
||||||
|
AttributeKeyOwner = "owner"
|
||||||
|
AttributeKeyBondId = "bond-id"
|
||||||
|
AttributeKeyPayload = "payload"
|
||||||
|
AttributeKeyOldBondId = "old-bond-id"
|
||||||
|
AttributeKeyNewBondId = "new-bond-id"
|
||||||
|
AttributeKeyCID = "cid"
|
||||||
|
AttributeKeyName = "name"
|
||||||
|
AttributeKeyCRN = "crn"
|
||||||
|
AttributeKeyRecordId = "record-id"
|
||||||
|
AttributeValueCategory = ModuleName
|
||||||
|
)
|
133
x/registry/helpers/helpers.go
Normal file
133
x/registry/helpers/helpers.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/gob"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
wnsUtils "git.vdb.to/cerc-io/laconic2d/utils"
|
||||||
|
set "github.com/deckarep/golang-set"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StringToBytes(val string) []byte {
|
||||||
|
return []byte(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BytesToString(val []byte) string {
|
||||||
|
return string(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StrArrToBytesArr(val []string) ([]byte, error) {
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
|
||||||
|
err := gob.NewEncoder(buffer).Encode(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buffer.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func BytesArrToStringArr(val []byte) ([]string, error) {
|
||||||
|
buffer := bytes.NewReader(val)
|
||||||
|
var v []string
|
||||||
|
err := gob.NewDecoder(buffer).Decode(&v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Int64ToBytes(num int64) []byte {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_ = binary.Write(buf, binary.BigEndian, num)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustMarshalJSON[T any](val T) (bytes []byte) {
|
||||||
|
bytes, err := json.Marshal(val)
|
||||||
|
if err != nil {
|
||||||
|
panic("JSON marshal error:" + err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustUnmarshalJSON[T any](bytes []byte) T {
|
||||||
|
var val T
|
||||||
|
err := json.Unmarshal(bytes, &val)
|
||||||
|
if err != nil {
|
||||||
|
panic("JSON unmarshal error:" + err.Error())
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCid gets the content ID.
|
||||||
|
func GetCid(content []byte) (string, error) {
|
||||||
|
return wnsUtils.CIDFromJSONBytes(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToBase64 encodes a byte array as a base64 string.
|
||||||
|
func BytesToBase64(bytes []byte) string {
|
||||||
|
return base64.StdEncoding.EncodeToString(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesFromBase64 decodes a byte array from a base64 string.
|
||||||
|
func BytesFromBase64(str string) []byte {
|
||||||
|
bytes, err := base64.StdEncoding.DecodeString(str)
|
||||||
|
if err != nil {
|
||||||
|
panic("Error decoding string to bytes.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToHex encodes a byte array as a hex string.
|
||||||
|
func BytesToHex(bytes []byte) string {
|
||||||
|
return hex.EncodeToString(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesFromHex decodes a byte array from a hex string.
|
||||||
|
func BytesFromHex(str string) []byte {
|
||||||
|
bytes, err := hex.DecodeString(str)
|
||||||
|
if err != nil {
|
||||||
|
panic("Error decoding hex to bytes.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetToSlice(set set.Set) []string {
|
||||||
|
names := []string{}
|
||||||
|
|
||||||
|
for name := range set.Iter() {
|
||||||
|
if name, ok := name.(string); ok && name != "" {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.SliceStable(names, func(i, j int) bool { return names[i] < names[j] })
|
||||||
|
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceToSet(names []string) set.Set {
|
||||||
|
set := set.NewThreadUnsafeSet()
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
if name != "" {
|
||||||
|
set.Add(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppendUnique(list []string, element string) []string {
|
||||||
|
set := SliceToSet(list)
|
||||||
|
set.Add(element)
|
||||||
|
|
||||||
|
return SetToSlice(set)
|
||||||
|
}
|
@ -1,19 +1,54 @@
|
|||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"cosmossdk.io/collections"
|
"cosmossdk.io/collections"
|
||||||
|
"cosmossdk.io/collections/indexes"
|
||||||
storetypes "cosmossdk.io/core/store"
|
storetypes "cosmossdk.io/core/store"
|
||||||
|
errorsmod "cosmossdk.io/errors"
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec/legacy"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
auth "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
auth "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
||||||
bank "github.com/cosmos/cosmos-sdk/x/bank/keeper"
|
bank "github.com/cosmos/cosmos-sdk/x/bank/keeper"
|
||||||
|
"github.com/gibson042/canonicaljson-go"
|
||||||
|
"github.com/ipld/go-ipld-prime"
|
||||||
|
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||||
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
|
|
||||||
auctionkeeper "git.vdb.to/cerc-io/laconic2d/x/auction/keeper"
|
auctionkeeper "git.vdb.to/cerc-io/laconic2d/x/auction/keeper"
|
||||||
bondkeeper "git.vdb.to/cerc-io/laconic2d/x/bond/keeper"
|
bondkeeper "git.vdb.to/cerc-io/laconic2d/x/bond/keeper"
|
||||||
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
||||||
|
"git.vdb.to/cerc-io/laconic2d/x/registry/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Add required methods
|
// TODO: Add required methods
|
||||||
|
|
||||||
|
type RecordsIndexes struct {
|
||||||
|
BondId *indexes.Multi[string, string, registrytypes.Record]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b RecordsIndexes) IndexesList() []collections.Index[string, registrytypes.Record] {
|
||||||
|
return []collections.Index[string, registrytypes.Record]{b.BondId}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRecordIndexes(sb *collections.SchemaBuilder) RecordsIndexes {
|
||||||
|
return RecordsIndexes{
|
||||||
|
BondId: indexes.NewMulti(
|
||||||
|
sb, registrytypes.BondIdIndexPrefix, "records_by_bond_id",
|
||||||
|
collections.StringKey, collections.StringKey,
|
||||||
|
func(_ string, v registrytypes.Record) (string, error) {
|
||||||
|
return v.BondId, nil
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Keeper struct {
|
type Keeper struct {
|
||||||
cdc codec.BinaryCodec
|
cdc codec.BinaryCodec
|
||||||
|
|
||||||
@ -24,8 +59,9 @@ type Keeper struct {
|
|||||||
auctionKeeper auctionkeeper.Keeper
|
auctionKeeper auctionkeeper.Keeper
|
||||||
|
|
||||||
// state management
|
// state management
|
||||||
Schema collections.Schema
|
Schema collections.Schema
|
||||||
Params collections.Item[registrytypes.Params]
|
Params collections.Item[registrytypes.Params]
|
||||||
|
Records *collections.IndexedMap[string, registrytypes.Record, RecordsIndexes]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeeper creates a new Keeper instance
|
// NewKeeper creates a new Keeper instance
|
||||||
@ -40,8 +76,9 @@ func NewKeeper(
|
|||||||
) Keeper {
|
) Keeper {
|
||||||
sb := collections.NewSchemaBuilder(storeService)
|
sb := collections.NewSchemaBuilder(storeService)
|
||||||
k := Keeper{
|
k := Keeper{
|
||||||
cdc: cdc,
|
cdc: cdc,
|
||||||
Params: collections.NewItem(sb, registrytypes.ParamsPrefix, "params", codec.CollValue[registrytypes.Params](cdc)),
|
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)),
|
||||||
}
|
}
|
||||||
|
|
||||||
schema, err := sb.Build()
|
schema, err := sb.Build()
|
||||||
@ -53,3 +90,167 @@ func NewKeeper(
|
|||||||
|
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasRecord - checks if a record by the given id exists.
|
||||||
|
func (k Keeper) HasRecord(ctx sdk.Context, id string) (bool, error) {
|
||||||
|
has, err := k.Records.Has(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return has, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutRecord - saves a record to the store.
|
||||||
|
func (k Keeper) SaveRecord(ctx sdk.Context, record registrytypes.Record) error {
|
||||||
|
return k.Records.Set(ctx, record.Id, record)
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// k.updateBlockChangeSetForRecord(ctx, record.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessSetRecord creates a record.
|
||||||
|
func (k Keeper) SetRecord(ctx sdk.Context, msg registrytypes.MsgSetRecord) (*registrytypes.ReadableRecord, error) {
|
||||||
|
payload := msg.Payload.ToReadablePayload()
|
||||||
|
record := registrytypes.ReadableRecord{Attributes: payload.RecordAttributes, BondId: msg.BondId}
|
||||||
|
|
||||||
|
// Check signatures.
|
||||||
|
resourceSignBytes, _ := record.GetSignBytes()
|
||||||
|
cid, err := record.GetCid()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Invalid record JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Id = cid
|
||||||
|
|
||||||
|
has, err := k.HasRecord(ctx, record.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if has {
|
||||||
|
// Immutable record already exists. No-op.
|
||||||
|
return &record, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Owners = []string{}
|
||||||
|
for _, sig := range payload.Signatures {
|
||||||
|
pubKey, err := legacy.PubKeyFromBytes(helpers.BytesFromBase64(sig.PubKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, fmt.Sprint("Error decoding pubKey from bytes: ", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
sigOK := pubKey.VerifySignature(resourceSignBytes, helpers.BytesFromBase64(sig.Sig))
|
||||||
|
if !sigOK {
|
||||||
|
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, fmt.Sprint("Signature mismatch: ", sig.PubKey))
|
||||||
|
}
|
||||||
|
record.Owners = append(record.Owners, pubKey.Address().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort owners list.
|
||||||
|
sort.Strings(record.Owners)
|
||||||
|
sdkErr := k.processRecord(ctx, &record, false)
|
||||||
|
if sdkErr != nil {
|
||||||
|
return nil, sdkErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return &record, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Keeper) processRecord(ctx sdk.Context, record *registrytypes.ReadableRecord, isRenewal bool) error {
|
||||||
|
params, err := k.GetParams(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rent := params.RecordRent
|
||||||
|
if err = k.bondKeeper.TransferCoinsToModuleAccount(
|
||||||
|
ctx, record.BondId, registrytypes.RecordRentModuleAccountName, sdk.NewCoins(rent),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
record.CreateTime = ctx.BlockHeader().Time.Format(time.RFC3339)
|
||||||
|
record.ExpiryTime = ctx.BlockHeader().Time.Add(params.RecordRentDuration).Format(time.RFC3339)
|
||||||
|
record.Deleted = false
|
||||||
|
|
||||||
|
recordObj, err := record.ToRecordObj()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save record in store.
|
||||||
|
if err = k.SaveRecord(ctx, recordObj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO look up/validate record type here
|
||||||
|
|
||||||
|
if err := k.processAttributes(ctx, record.Attributes, record.Id, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// expiryTimeKey := GetAttributesIndexKey(ExpiryTimeAttributeName, []byte(record.ExpiryTime))
|
||||||
|
// if err := k.SetAttributeMapping(ctx, expiryTimeKey, record.ID); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// k.InsertRecordExpiryQueue(ctx, recordObj)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Keeper) processAttributes(ctx sdk.Context, attrs registrytypes.AttributeMap, id string, prefix string) error {
|
||||||
|
np := basicnode.Prototype.Map
|
||||||
|
nb := np.NewBuilder()
|
||||||
|
encAttrs, err := canonicaljson.Marshal(attrs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(attrs) == 0 {
|
||||||
|
encAttrs = []byte("{}")
|
||||||
|
}
|
||||||
|
err = dagjson.Decode(nb, bytes.NewReader(encAttrs))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to decode attributes: %w", err)
|
||||||
|
}
|
||||||
|
n := nb.Build()
|
||||||
|
if n.Kind() != ipld.Kind_Map {
|
||||||
|
return fmt.Errorf("record attributes must be a map, not %T", n.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
|
return k.processAttributeMap(ctx, n, id, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Keeper) processAttributeMap(ctx sdk.Context, n ipld.Node, id string, prefix string) error {
|
||||||
|
for it := n.MapIterator(); !it.Done(); {
|
||||||
|
//nolint:misspell
|
||||||
|
keynode, valuenode, err := it.Next()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key, err := keynode.AsString()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if valuenode.Kind() == ipld.Kind_Map {
|
||||||
|
err := k.processAttributeMap(ctx, valuenode, id, key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := dagjson.Encode(valuenode, &buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
// value := buf.Bytes()
|
||||||
|
// indexKey := GetAttributesIndexKey(prefix+key, value)
|
||||||
|
// if err := k.SetAttributeMapping(ctx, indexKey, id); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,14 +1,287 @@
|
|||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
// import "git.vdb.to/cerc-io/laconic2d/x/registry"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
// var _ registry.MsgServer = msgServer{}
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ registrytypes.MsgServer = msgServer{}
|
||||||
|
|
||||||
type msgServer struct {
|
type msgServer struct {
|
||||||
k Keeper
|
k Keeper
|
||||||
}
|
}
|
||||||
|
|
||||||
// // NewMsgServerImpl returns an implementation of the module MsgServer interface.
|
// NewMsgServerImpl returns an implementation of the module MsgServer interface.
|
||||||
// func NewMsgServerImpl(keeper Keeper) registry.MsgServer {
|
func NewMsgServerImpl(keeper Keeper) registrytypes.MsgServer {
|
||||||
// return &msgServer{k: keeper}
|
return &msgServer{k: keeper}
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
record, err := ms.k.SetRecord(ctx, *msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeSetRecord,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.GetSigner()),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyBondId, msg.GetBondId()),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyPayload, msg.Payload.Record.Id),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
return ®istrytypes.MsgSetRecordResponse{Id: record.Id}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeSetRecord,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyCRN, msg.Crn),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyCID, msg.Cid),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return ®istrytypes.MsgSetNameResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
_, err = sdk.AccAddressFromBech32(msg.Owner)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = ms.k.ProcessReserveAuthority(ctx, *msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeReserveNameAuthority,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyName, msg.Name),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyOwner, msg.Owner),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return ®istrytypes.MsgReserveAuthorityResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
err = ms.k.ProcessSetAuthorityBond(ctx, *msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeAuthorityBond,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyName, msg.Name),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyBondId, msg.BondId),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return ®istrytypes.MsgSetAuthorityBondResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeDeleteName,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyCRN, msg.Crn),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return ®istrytypes.MsgDeleteNameAuthorityResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms msgServer) RenewRecord(c context.Context, msg *registrytypes.MsgRenewRecord) (*registrytypes.MsgRenewRecordResponse, error) {
|
||||||
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
_, err := sdk.AccAddressFromBech32(msg.Signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = ms.k.ProcessRenewRecord(ctx, *msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeRenewRecord,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyRecordId, msg.RecordId),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return ®istrytypes.MsgRenewRecordResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: all
|
||||||
|
func (ms msgServer) AssociateBond(c context.Context, msg *registrytypes.MsgAssociateBond) (*registrytypes.MsgAssociateBondResponse, error) {
|
||||||
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
_, err := sdk.AccAddressFromBech32(msg.Signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ms.k.ProcessAssociateBond(ctx, *msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeAssociateBond,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyRecordId, msg.RecordId),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyBondId, msg.BondId),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return ®istrytypes.MsgAssociateBondResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms msgServer) DissociateBond(c context.Context, msg *registrytypes.MsgDissociateBond) (*registrytypes.MsgDissociateBondResponse, error) {
|
||||||
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
_, err := sdk.AccAddressFromBech32(msg.Signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = ms.k.ProcessDissociateBond(ctx, *msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeDissociateBond,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyRecordId, msg.RecordId),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return ®istrytypes.MsgDissociateBondResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms msgServer) DissociateRecords(c context.Context, msg *registrytypes.MsgDissociateRecords) (*registrytypes.MsgDissociateRecordsResponse, error) {
|
||||||
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
_, err := sdk.AccAddressFromBech32(msg.Signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = ms.k.ProcessDissociateRecords(ctx, *msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeDissociateRecords,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyBondId, msg.BondId),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return ®istrytypes.MsgDissociateRecordsResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms msgServer) ReAssociateRecords(c context.Context, msg *registrytypes.MsgReAssociateRecords) (*registrytypes.MsgReAssociateRecordsResponse, error) { //nolint: all
|
||||||
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
_, err := sdk.AccAddressFromBech32(msg.Signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = ms.k.ProcessReAssociateRecords(ctx, *msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
|
sdk.NewEvent(
|
||||||
|
registrytypes.EventTypeReAssociateRecords,
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyOldBondId, msg.OldBondId),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeyNewBondId, msg.NewBondId),
|
||||||
|
),
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, registrytypes.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(registrytypes.AttributeKeySigner, msg.Signer),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
return ®istrytypes.MsgReAssociateRecordsResponse{}, nil
|
||||||
|
}
|
||||||
|
26
x/registry/keeper/naming_keeper.go
Normal file
26
x/registry/keeper/naming_keeper.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessSetName creates a CRN -> Record ID mapping.
|
||||||
|
func (k Keeper) ProcessSetName(ctx sdk.Context, msg registrytypes.MsgSetName) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessReserveAuthority reserves a name authority.
|
||||||
|
func (k Keeper) ProcessReserveAuthority(ctx sdk.Context, msg registrytypes.MsgReserveAuthority) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Keeper) ProcessSetAuthorityBond(ctx sdk.Context, msg registrytypes.MsgSetAuthorityBond) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessDeleteName removes a CRN -> Record ID mapping.
|
||||||
|
func (k Keeper) ProcessDeleteName(ctx sdk.Context, msg registrytypes.MsgDeleteNameAuthority) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
20
x/registry/keeper/params.go
Normal file
20
x/registry/keeper/params.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetParams - Get all parameters as types.Params.
|
||||||
|
func (k Keeper) GetParams(ctx sdk.Context) (*registrytypes.Params, error) {
|
||||||
|
params, err := k.Params.Get(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ¶ms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetParams - set the params.
|
||||||
|
func (k Keeper) SetParams(ctx sdk.Context, params registrytypes.Params) {
|
||||||
|
}
|
@ -1,8 +1,12 @@
|
|||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
// import (
|
import (
|
||||||
// registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
"context"
|
||||||
// )
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
// var _ registrytypes.QueryServer = queryServer{}
|
// var _ registrytypes.QueryServer = queryServer{}
|
||||||
|
|
||||||
@ -14,3 +18,16 @@ type queryServer struct {
|
|||||||
// func NewQueryServerImpl(k Keeper) registrytypes.QueryServer {
|
// func NewQueryServerImpl(k Keeper) registrytypes.QueryServer {
|
||||||
// return queryServer{k}
|
// return queryServer{k}
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
func (qs queryServer) Params(c context.Context, _ *registrytypes.QueryParamsRequest) (*registrytypes.QueryParamsResponse, error) {
|
||||||
|
ctx := sdk.UnwrapSDKContext(c)
|
||||||
|
|
||||||
|
params, err := qs.k.GetParams(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ®istrytypes.QueryParamsResponse{Params: params}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add remaining methods
|
||||||
|
@ -2,8 +2,10 @@ package keeper
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
auctionkeeper "git.vdb.to/cerc-io/laconic2d/x/auction/keeper"
|
auctionkeeper "git.vdb.to/cerc-io/laconic2d/x/auction/keeper"
|
||||||
|
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Add methods
|
// TODO: Add methods
|
||||||
@ -14,3 +16,28 @@ type RecordKeeper struct {
|
|||||||
auctionKeeper auctionkeeper.Keeper
|
auctionKeeper auctionkeeper.Keeper
|
||||||
// storeKey storetypes.StoreKey // Unexposed key to access store from sdk.Context
|
// storeKey storetypes.StoreKey // Unexposed key to access store from sdk.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProcessRenewRecord renews a record.
|
||||||
|
func (k Keeper) ProcessRenewRecord(ctx sdk.Context, msg registrytypes.MsgRenewRecord) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessAssociateBond associates a record with a bond.
|
||||||
|
func (k Keeper) ProcessAssociateBond(ctx sdk.Context, msg registrytypes.MsgAssociateBond) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessDissociateBond dissociates a record from its bond.
|
||||||
|
func (k Keeper) ProcessDissociateBond(ctx sdk.Context, msg registrytypes.MsgDissociateBond) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessDissociateRecords dissociates all records associated with a given bond.
|
||||||
|
func (k Keeper) ProcessDissociateRecords(ctx sdk.Context, msg registrytypes.MsgDissociateRecords) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessReAssociateRecords switches records from and old to new bond.
|
||||||
|
func (k Keeper) ProcessReAssociateRecords(ctx sdk.Context, msg registrytypes.MsgReAssociateRecords) error {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
@ -17,4 +17,7 @@ const (
|
|||||||
var (
|
var (
|
||||||
// ParamsKey is the prefix for params key
|
// ParamsKey is the prefix for params key
|
||||||
ParamsPrefix = collections.NewPrefix(0)
|
ParamsPrefix = collections.NewPrefix(0)
|
||||||
|
|
||||||
|
RecordsPrefix = collections.NewPrefix(1)
|
||||||
|
BondIdIndexPrefix = collections.NewPrefix(2)
|
||||||
)
|
)
|
||||||
|
@ -3,7 +3,8 @@ package module
|
|||||||
import (
|
import (
|
||||||
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
|
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
|
||||||
"cosmossdk.io/client/v2/autocli"
|
"cosmossdk.io/client/v2/autocli"
|
||||||
// registryv1 "git.vdb.to/cerc-io/laconic2d/api/cerc/registry/v1"
|
|
||||||
|
registryv1 "git.vdb.to/cerc-io/laconic2d/api/cerc/registry/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ autocli.HasAutoCLIConfig = AppModule{}
|
var _ autocli.HasAutoCLIConfig = AppModule{}
|
||||||
@ -11,7 +12,14 @@ var _ autocli.HasAutoCLIConfig = AppModule{}
|
|||||||
// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
|
// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
|
||||||
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
|
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
|
||||||
return &autocliv1.ModuleOptions{
|
return &autocliv1.ModuleOptions{
|
||||||
Query: nil,
|
Query: &autocliv1.ServiceCommandDescriptor{
|
||||||
Tx: nil,
|
Service: registryv1.Query_ServiceDesc.ServiceName,
|
||||||
|
RpcCommandOptions: []*autocliv1.RpcCommandOptions{},
|
||||||
|
},
|
||||||
|
Tx: &autocliv1.ServiceCommandDescriptor{
|
||||||
|
Service: registryv1.Msg_ServiceDesc.ServiceName,
|
||||||
|
RpcCommandOptions: []*autocliv1.RpcCommandOptions{},
|
||||||
|
EnhanceCustomCommand: true, // Allow additional manual commands
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"cosmossdk.io/core/appmodule"
|
"cosmossdk.io/core/appmodule"
|
||||||
gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
|
gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
"github.com/cosmos/cosmos-sdk/types/module"
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
|
|
||||||
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
registrytypes "git.vdb.to/cerc-io/laconic2d/x/registry"
|
||||||
|
"git.vdb.to/cerc-io/laconic2d/x/registry/client/cli"
|
||||||
"git.vdb.to/cerc-io/laconic2d/x/registry/keeper"
|
"git.vdb.to/cerc-io/laconic2d/x/registry/keeper"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -115,7 +117,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw
|
|||||||
// RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries.
|
// RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries.
|
||||||
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
func (am AppModule) RegisterServices(cfg module.Configurator) {
|
||||||
// Register servers
|
// Register servers
|
||||||
// registrytypes.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
registrytypes.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
|
||||||
// registrytypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper))
|
// registrytypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,3 +126,8 @@ func (am AppModule) RegisterServices(cfg module.Configurator) {
|
|||||||
func (am AppModule) EndBlock(ctx context.Context) error {
|
func (am AppModule) EndBlock(ctx context.Context) error {
|
||||||
return EndBlocker(ctx, am.keeper)
|
return EndBlocker(ctx, am.keeper)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the root tx command of this module
|
||||||
|
func (AppModule) GetTxCmd() *cobra.Command {
|
||||||
|
return cli.GetTxCmd()
|
||||||
|
}
|
||||||
|
35
x/registry/msgs.go
Normal file
35
x/registry/msgs.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
errorsmod "cosmossdk.io/errors"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewMsgSetRecord is the constructor function for MsgSetRecord.
|
||||||
|
func NewMsgSetRecord(payload Payload, bondId string, signer sdk.AccAddress) *MsgSetRecord {
|
||||||
|
return &MsgSetRecord{
|
||||||
|
Payload: payload,
|
||||||
|
BondId: bondId,
|
||||||
|
Signer: signer.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg MsgSetRecord) ValidateBasic() error {
|
||||||
|
if len(msg.Signer) == 0 {
|
||||||
|
return errorsmod.Wrap(sdkerrors.ErrInvalidAddress, msg.Signer)
|
||||||
|
}
|
||||||
|
|
||||||
|
owners := msg.Payload.Record.Owners
|
||||||
|
for _, owner := range owners {
|
||||||
|
if owner == "" {
|
||||||
|
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Record owner not set.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(msg.BondId) == 0 {
|
||||||
|
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Bond Id is required.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
137
x/registry/types.go
Normal file
137
x/registry/types.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
|
||||||
|
"github.com/gibson042/canonicaljson-go"
|
||||||
|
|
||||||
|
"git.vdb.to/cerc-io/laconic2d/x/registry/helpers"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AuthorityActive = "active"
|
||||||
|
AuthorityExpired = "expired"
|
||||||
|
AuthorityUnderAuction = "auction"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO if schema records are to be more permissive than allowing a map of fields, this type will
|
||||||
|
// become specific to content records. schema records will either occupy a new message or have new
|
||||||
|
// more general purpose helper types.
|
||||||
|
|
||||||
|
type AttributeMap map[string]interface{}
|
||||||
|
|
||||||
|
// ReadablePayload represents a signed record payload that can be serialized from/to YAML.
|
||||||
|
type ReadablePayload struct {
|
||||||
|
RecordAttributes AttributeMap `json:"record" yaml:"record"`
|
||||||
|
Signatures []Signature `json:"signatures" yaml:"signatures"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadableRecord represents a WNS record.
|
||||||
|
type ReadableRecord struct {
|
||||||
|
Id string `json:"id,omitempty"`
|
||||||
|
Names []string `json:"names,omitempty"`
|
||||||
|
BondId string `json:"bond_id,omitempty"`
|
||||||
|
CreateTime string `json:"create_time,omitempty"`
|
||||||
|
ExpiryTime string `json:"expiry_time,omitempty"`
|
||||||
|
Deleted bool `json:"deleted,omitempty"`
|
||||||
|
Owners []string `json:"owners,omitempty"`
|
||||||
|
Attributes AttributeMap `json:"attributes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPayload converts PayloadEncodable to Payload object.
|
||||||
|
// Why? Because go-amino can't handle maps: https://github.com/tendermint/go-amino/issues/4.
|
||||||
|
func (payloadObj *ReadablePayload) ToPayload() Payload {
|
||||||
|
// Note: record directly contains the attributes here
|
||||||
|
attributes := payloadObj.RecordAttributes
|
||||||
|
payload := Payload{
|
||||||
|
Record: &Record{
|
||||||
|
Deleted: false,
|
||||||
|
Owners: nil,
|
||||||
|
Attributes: helpers.MustMarshalJSON(attributes),
|
||||||
|
},
|
||||||
|
Signatures: payloadObj.Signatures,
|
||||||
|
}
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToReadablePayload converts Payload to a serializable object
|
||||||
|
func (payload Payload) ToReadablePayload() ReadablePayload {
|
||||||
|
var encodable ReadablePayload
|
||||||
|
|
||||||
|
encodable.RecordAttributes = helpers.MustUnmarshalJSON[AttributeMap](payload.Record.Attributes)
|
||||||
|
encodable.Signatures = payload.Signatures
|
||||||
|
|
||||||
|
return encodable
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToRecordObj converts Record to RecordObj.
|
||||||
|
// Why? Because go-amino can't handle maps: https://github.com/tendermint/go-amino/issues/4.
|
||||||
|
func (r *ReadableRecord) ToRecordObj() (Record, error) {
|
||||||
|
var resourceObj Record
|
||||||
|
|
||||||
|
resourceObj.Id = r.Id
|
||||||
|
resourceObj.BondId = r.BondId
|
||||||
|
resourceObj.CreateTime = r.CreateTime
|
||||||
|
resourceObj.ExpiryTime = r.ExpiryTime
|
||||||
|
resourceObj.Deleted = r.Deleted
|
||||||
|
resourceObj.Owners = r.Owners
|
||||||
|
resourceObj.Attributes = helpers.MustMarshalJSON(r.Attributes)
|
||||||
|
|
||||||
|
return resourceObj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToReadableRecord converts Record to a serializable object
|
||||||
|
func (r *Record) ToReadableRecord() ReadableRecord {
|
||||||
|
var resourceObj ReadableRecord
|
||||||
|
|
||||||
|
resourceObj.Id = r.Id
|
||||||
|
resourceObj.BondId = r.BondId
|
||||||
|
resourceObj.CreateTime = r.CreateTime
|
||||||
|
resourceObj.ExpiryTime = r.ExpiryTime
|
||||||
|
resourceObj.Deleted = r.Deleted
|
||||||
|
resourceObj.Owners = r.Owners
|
||||||
|
resourceObj.Names = r.Names
|
||||||
|
resourceObj.Attributes = helpers.MustUnmarshalJSON[AttributeMap](r.Attributes)
|
||||||
|
|
||||||
|
return resourceObj
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanonicalJSON returns the canonical JSON representation of the record.
|
||||||
|
func (r *ReadableRecord) CanonicalJSON() []byte {
|
||||||
|
bytes, err := canonicaljson.Marshal(r.Attributes)
|
||||||
|
if err != nil {
|
||||||
|
panic("error marshaling record: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSignBytes generates a record hash to be signed.
|
||||||
|
func (r *ReadableRecord) GetSignBytes() ([]byte, []byte) {
|
||||||
|
// Double SHA256 hash.
|
||||||
|
|
||||||
|
// Input to the first round of hashing.
|
||||||
|
bytes := r.CanonicalJSON()
|
||||||
|
|
||||||
|
// First round.
|
||||||
|
first := sha256.New()
|
||||||
|
first.Write(bytes)
|
||||||
|
firstHash := first.Sum(nil)
|
||||||
|
|
||||||
|
// Second round of hashing takes as input the output of the first round.
|
||||||
|
second := sha256.New()
|
||||||
|
second.Write(firstHash)
|
||||||
|
secondHash := second.Sum(nil)
|
||||||
|
|
||||||
|
return secondHash, bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCid gets the record CID.
|
||||||
|
func (r *ReadableRecord) GetCid() (string, error) {
|
||||||
|
id, err := helpers.GetCid(r.CanonicalJSON())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return id, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user