From d64e80f963ef2db234c3efd83cd6b14725904b5d Mon Sep 17 00:00:00 2001 From: Roy Crihfield Date: Thu, 26 Oct 2023 08:45:08 -0500 Subject: [PATCH] clean up --- x/registry/helpers/helpers.go | 14 ++--- x/registry/keeper/grpc_query_test.go | 10 ++-- x/registry/keeper/keeper.go | 50 ++++++++++-------- x/registry/types/types.go | 79 +++++++++++++--------------- 4 files changed, 77 insertions(+), 76 deletions(-) diff --git a/x/registry/helpers/helpers.go b/x/registry/helpers/helpers.go index 07041252..3d993f80 100644 --- a/x/registry/helpers/helpers.go +++ b/x/registry/helpers/helpers.go @@ -47,24 +47,20 @@ func Int64ToBytes(num int64) []byte { return buf.Bytes() } -// MarshalMapToJSONBytes converts map[string]interface{} to bytes. -func MarshalMapToJSONBytes(val map[string]interface{}) (bytes []byte) { +func MustMarshalJSON[T any](val T) (bytes []byte) { bytes, err := json.Marshal(val) if err != nil { - panic("Marshal error.") + panic("JSON marshal error:" + err.Error()) } - return } -// UnMarshalMapFromJSONBytes converts bytes to map[string]interface{}. -func UnMarshalMapFromJSONBytes(bytes []byte) map[string]interface{} { - var val map[string]interface{} +func MustUnmarshalJSON[T any](bytes []byte) T { + var val T err := json.Unmarshal(bytes, &val) if err != nil { - panic("Unmarshal error.") + panic("JSON unmarshal error:" + err.Error()) } - return val } diff --git a/x/registry/keeper/grpc_query_test.go b/x/registry/keeper/grpc_query_test.go index e51a9326..ca48f179 100644 --- a/x/registry/keeper/grpc_query_test.go +++ b/x/registry/keeper/grpc_query_test.go @@ -126,12 +126,16 @@ func (suite *KeeperTestSuite) TestGrpcGetRecordLists() { sr.Equal(resp.GetRecords()[0].GetBondId(), suite.bond.GetId()) for _, record := range resp.GetRecords() { - recAttr := helpers.UnMarshalMapFromJSONBytes(record.Attributes) + recAttr := helpers.MustUnmarshalJSON[registrytypes.AttributeMap](record.Attributes) + for _, attr := range test.req.GetAttributes() { + enc, err := keeper.QueryValueToJSON(attr.Value) + sr.NoError(err) + expected := helpers.MustUnmarshalJSON[any](enc) if attr.Key[:4] == "x500" { - sr.Equal(keeper.EncodeAttributeValue(attr.Value), recAttr["x500"].(map[string]interface{})[attr.Key[4:]]) + sr.Equal(expected, recAttr["x500"].(map[string]interface{})[attr.Key[4:]]) } else { - sr.Equal(keeper.EncodeAttributeValue(attr.Value), recAttr[attr.Key]) + sr.Equal(expected, recAttr[attr.Key]) } } } diff --git a/x/registry/keeper/keeper.go b/x/registry/keeper/keeper.go index 645a9820..4eaac851 100644 --- a/x/registry/keeper/keeper.go +++ b/x/registry/keeper/keeper.go @@ -8,10 +8,6 @@ import ( "time" errorsmod "cosmossdk.io/errors" - auctionkeeper "github.com/cerc-io/laconicd/x/auction/keeper" - bondkeeper "github.com/cerc-io/laconicd/x/bond/keeper" - "github.com/cerc-io/laconicd/x/registry/helpers" - "github.com/cerc-io/laconicd/x/registry/types" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/legacy" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -20,13 +16,18 @@ import ( auth "github.com/cosmos/cosmos-sdk/x/auth/keeper" bank "github.com/cosmos/cosmos-sdk/x/bank/keeper" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - "github.com/tendermint/tendermint/libs/log" - + "github.com/gibson042/canonicaljson-go" cid "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/codec/dagjson" cidlink "github.com/ipld/go-ipld-prime/linking/cid" basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/tendermint/tendermint/libs/log" + + auctionkeeper "github.com/cerc-io/laconicd/x/auction/keeper" + bondkeeper "github.com/cerc-io/laconicd/x/bond/keeper" + "github.com/cerc-io/laconicd/x/registry/helpers" + "github.com/cerc-io/laconicd/x/registry/types" ) var ( @@ -152,7 +153,7 @@ func (k Keeper) ListRecords(ctx sdk.Context) []types.Record { func (k Keeper) RecordsFromAttributes(ctx sdk.Context, attributes []*types.QueryListRecordsRequest_KeyValueInput, all bool) ([]types.Record, error) { resultRecordIds := []string{} for i, attr := range attributes { - suffix, err := EncodeQueryValue(attr.Value) + suffix, err := QueryValueToJSON(attr.Value) if err != nil { return nil, err } @@ -184,8 +185,9 @@ func (k Keeper) RecordsFromAttributes(ctx sdk.Context, attributes []*types.Query return records, nil } -// TODO not recursive, and only should be if we want to support querying with whole sub-objects -func EncodeQueryValue(input *types.QueryListRecordsRequest_ValueInput) ([]byte, error) { +// TODO not recursive, and only should be if we want to support querying with whole sub-objects, +// which seems unnecessary. +func QueryValueToJSON(input *types.QueryListRecordsRequest_ValueInput) ([]byte, error) { np := basicnode.Prototype.Any nb := np.NewBuilder() @@ -202,20 +204,19 @@ func EncodeQueryValue(input *types.QueryListRecordsRequest_ValueInput) ([]byte, link := cidlink.Link{Cid: cid.MustParse(value.Link)} nb.AssignLink(link) case *types.QueryListRecordsRequest_ValueInput_Array: - // TODO + return nil, fmt.Errorf("Recursive query values are not supported") case *types.QueryListRecordsRequest_ValueInput_Map: - // TODO + return nil, fmt.Errorf("Recursive query values are not supported") default: - return nil, fmt.Errorf("Value has unepxpected type %T", value) + return nil, fmt.Errorf("Value has unexpected type %T", value) } n := nb.Build() var buf bytes.Buffer if err := dagjson.Encode(n, &buf); err != nil { - return nil, err + return nil, fmt.Errorf("encoding value to JSON failed: %w", err) } - value := buf.Bytes() - return value, nil + return buf.Bytes(), nil } func getIntersection(a []string, b []string) []string { @@ -288,14 +289,12 @@ func (k Keeper) ProcessSetRecord(ctx sdk.Context, msg types.MsgSetRecord) (*type for _, sig := range payload.Signatures { pubKey, err := legacy.PubKeyFromBytes(helpers.BytesFromBase64(sig.PubKey)) if err != nil { - fmt.Println("Error decoding pubKey from bytes: ", err) - return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Invalid public key.") + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, fmt.Sprint("Error decoding pubKey from bytes: ", err)) } sigOK := pubKey.VerifySignature(resourceSignBytes, helpers.BytesFromBase64(sig.Sig)) if !sigOK { - fmt.Println("Signature mismatch: ", sig.PubKey) - return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Invalid signature.") + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, fmt.Sprint("Signature mismatch: ", sig.PubKey)) } record.Owners = append(record.Owners, pubKey.Address().String()) } @@ -359,17 +358,25 @@ func (k Keeper) PutRecord(ctx sdk.Context, record types.Record) { k.updateBlockChangeSetForRecord(ctx, record.Id) } -func (k Keeper) processAttributes(ctx sdk.Context, attrs []byte, id string, prefix string) error { +func (k Keeper) processAttributes(ctx sdk.Context, attrs types.AttributeMap, id string, prefix string) error { np := basicnode.Prototype.Map nb := np.NewBuilder() - err := dagjson.Decode(nb, bytes.NewReader(attrs)) + 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) } @@ -404,7 +411,6 @@ func (k Keeper) processAttributeMap(ctx sdk.Context, n ipld.Node, id string, pre func GetAttributesIndexKey(key string, suffix []byte) []byte { keyString := fmt.Sprintf("%s%s", key, suffix) return append(PrefixAttributesIndex, []byte(keyString)...) - // return append(append(PrefixAttributesIndex, key...), suffix...) } func (k Keeper) SetAttributeMapping(ctx sdk.Context, key []byte, recordID string) error { diff --git a/x/registry/types/types.go b/x/registry/types/types.go index 2c7d5bb4..9bb4fb71 100644 --- a/x/registry/types/types.go +++ b/x/registry/types/types.go @@ -4,7 +4,7 @@ import ( "crypto/sha256" "github.com/cerc-io/laconicd/x/registry/helpers" - canonicalJson "github.com/gibson042/canonicaljson-go" + "github.com/gibson042/canonicaljson-go" ) const ( @@ -17,16 +17,24 @@ const ( // become specific to content records. schema records will either occupy a new message or have new // more general purpose helper types. -type DagJsonBlob []byte - -func (b DagJsonBlob) MarshalJSON() ([]byte, error) { - return b, nil -} +type AttributeMap map[string]interface{} // PayloadEncodable represents a signed record payload that can be serialized from/to YAML. type PayloadEncodable struct { - RecordAttributes DagJsonBlob `json:"record"` - Signatures []Signature `json:"signatures"` + RecordAttributes AttributeMap `json:"record" yaml:"record"` + Signatures []Signature `json:"signatures" yaml:"signatures"` +} + +// RecordEncodable represents a WNS record. +type RecordEncodable struct { + ID string `json:"id,omitempty"` + Names []string `json:"names,omitempty"` + BondID string `json:"bondId,omitempty"` + CreateTime string `json:"createTime,omitempty"` + ExpiryTime string `json:"expiryTime,omitempty"` + Deleted bool `json:"deleted,omitempty"` + Owners []string `json:"owners,omitempty"` + Attributes AttributeMap `json:"attributes,omitempty"` } // ToPayload converts PayloadEncodable to Payload object. @@ -38,7 +46,7 @@ func (payloadObj *PayloadEncodable) ToPayload() Payload { Record: &Record{ Deleted: false, Owners: nil, - Attributes: attributes, + Attributes: helpers.MustMarshalJSON(attributes), }, Signatures: payloadObj.Signatures, } @@ -49,41 +57,12 @@ func (payloadObj *PayloadEncodable) ToPayload() Payload { func (payload Payload) ToReadablePayload() PayloadEncodable { var encodable PayloadEncodable - encodable.RecordAttributes = payload.Record.Attributes + encodable.RecordAttributes = helpers.MustUnmarshalJSON[AttributeMap](payload.Record.Attributes) encodable.Signatures = payload.Signatures return encodable } -// ToReadableRecord converts Record to a serializable object -func (r *Record) ToReadableRecord() RecordEncodable { - var resourceObj RecordEncodable - - 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 = r.Attributes - - return resourceObj -} - -// RecordEncodable represents a WNS record. -type RecordEncodable struct { - ID string `json:"id,omitempty"` - Names []string `json:"names,omitempty"` - BondID string `json:"bondId,omitempty"` - CreateTime string `json:"createTime,omitempty"` - ExpiryTime string `json:"expiryTime,omitempty"` - Deleted bool `json:"deleted,omitempty"` - Owners []string `json:"owners,omitempty"` - // Attributes map[string]interface{} `json:"attributes,omitempty"` - Attributes DagJsonBlob `json:"attributes,omitempty"` -} - // ToRecordObj converts Record to RecordObj. // Why? Because go-amino can't handle maps: https://github.com/tendermint/go-amino/issues/4. func (r *RecordEncodable) ToRecordObj() (Record, error) { @@ -95,16 +74,32 @@ func (r *RecordEncodable) ToRecordObj() (Record, error) { resourceObj.ExpiryTime = r.ExpiryTime resourceObj.Deleted = r.Deleted resourceObj.Owners = r.Owners - resourceObj.Attributes = r.Attributes + resourceObj.Attributes = helpers.MustMarshalJSON(r.Attributes) return resourceObj, nil } +// ToReadableRecord converts Record to a serializable object +func (r *Record) ToReadableRecord() RecordEncodable { + var resourceObj RecordEncodable + + 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 *RecordEncodable) CanonicalJSON() []byte { - bytes, err := canonicalJson.Marshal(r.Attributes) + bytes, err := canonicaljson.Marshal(r.Attributes) if err != nil { - panic("Record marshal error: " + err.Error()) + panic("error marshalling record: " + err.Error()) } return bytes