From 8bdd7cb915f8e8d4103dc4d21b212aae67644b56 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 17 Jul 2023 12:39:14 -0400 Subject: [PATCH 01/11] feat(cosmos): Add a vstorage package for decoding CapData --- golang/cosmos/x/vstorage/capdata/capdata.go | 283 ++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 golang/cosmos/x/vstorage/capdata/capdata.go diff --git a/golang/cosmos/x/vstorage/capdata/capdata.go b/golang/cosmos/x/vstorage/capdata/capdata.go new file mode 100644 index 00000000000..3fa1c7be7a1 --- /dev/null +++ b/golang/cosmos/x/vstorage/capdata/capdata.go @@ -0,0 +1,283 @@ +package capdata + +import ( + "encoding/json" + "fmt" + "regexp" + "strconv" + "strings" +) + +// cf. https://github.com/endojs/endo/tree/master/packages/marshal + +type Capdata struct { + Body string `json:"body"` + Slots []interface{} `json:"slots"` +} + +var validBigint = regexp.MustCompile(`^-?(?:0|[1-9][0-9]*)$`) + +type CapdataBigint struct { + Normalized string +} + +type CapdataRemotable struct { + Id interface{} + Iface *string + Representation interface{} +} + +func NewCapdataBigint(str string) *CapdataBigint { + if !validBigint.MatchString(str) { + return nil + } + bigint := CapdataBigint{str} + return &bigint +} + +func (r *CapdataRemotable) MarshalJSON() ([]byte, error) { + return json.Marshal(r.Representation) +} + +type CapdataValueTransformations struct { + Bigint func(*CapdataBigint) interface{} + Remotable func(*CapdataRemotable) interface{} +} + +// upsertCapdataRemotable either adds a new CapdataRemotable to `remotables` at the specified +// slot index or updates the iface of the value that is already there, ensuring lack of iface name +// inconsistency (iteration order is not guaranteed to correspond with JSON text like it does in +// JavaScript, so we must accept encountering the "first" reference to a slot late +// and must therefore also defer transformations). +func upsertCapdataRemotable(remotables map[uint64]*CapdataRemotable, slotIndex uint64, id interface{}, iface *string) (*CapdataRemotable, error) { + r := remotables[slotIndex] + if r == nil { + r = new(CapdataRemotable) + r.Id = id + r.Iface = iface + remotables[slotIndex] = r + } else if iface != nil { + if r.Iface != nil && *iface != *r.Iface { + return nil, fmt.Errorf("slot iface mismatch: %q", *iface) + } + r.Iface = iface + } + return r, nil +} + +// decodeCapdataLegacyValue decodes the non-smallcaps encoding of +// https://github.com/endojs/endo/blob/master/packages/marshal/src/encodeToCapData.js +func decodeCapdataLegacyValue( + encoded interface{}, + slots []interface{}, + remotables map[uint64]*CapdataRemotable, + transformations CapdataValueTransformations, +) (interface{}, error) { + if arr, ok := encoded.([]interface{}); ok { + for i, v := range arr { + decoded, err := decodeCapdataLegacyValue(v, slots, remotables, transformations) + if err != nil { + return nil, err + } + arr[i] = decoded + } + return arr, nil + } else if obj, ok := encoded.(map[string]interface{}); ok { + if qclassVal, ok := obj["@qclass"]; ok { + qclass, ok := qclassVal.(string) + if !ok { + return nil, fmt.Errorf("invalid @qclass: %q", qclassVal) + } + switch qclass { + case "bigint": + var bigint *CapdataBigint + digitsVal := obj["digits"] + if digitsStr, ok := digitsVal.(string); ok { + bigint = NewCapdataBigint(digitsStr) + } + if bigint == nil { + return nil, fmt.Errorf("invalid bigint: %q", digitsVal) + } + if transformations.Bigint == nil { + return nil, fmt.Errorf("untransformed bigint") + } + return transformations.Bigint(bigint), nil + case "slot": + var iface *string + slotIndexVal, ifaceVal := obj["index"], obj["iface"] + slotIndexNum, ok := slotIndexVal.(float64) + slotIndex := uint64(slotIndexNum) + if !ok || float64(slotIndex) != slotIndexNum || slotIndex >= uint64(len(slots)) { + return nil, fmt.Errorf("invalid slot index: %q", slotIndexVal) + } + if ifaceStr, ok := ifaceVal.(string); ok { + iface = &ifaceStr + } else if ifaceVal != nil { + return nil, fmt.Errorf("invalid iface: %q", ifaceVal) + } + return upsertCapdataRemotable(remotables, slotIndex, slots[slotIndex], iface) + case "hilbert": + fallthrough + case "undefined": + fallthrough + case "NaN": + fallthrough + case "Infinity": + fallthrough + case "symbol": + fallthrough + case "tagged": + fallthrough + case "error": + fallthrough + case "-Infinity": + return nil, fmt.Errorf("not implemented: @qclass %q", qclass) + default: + return nil, fmt.Errorf("invalid @qclass: %q", qclass) + } + } + for k, v := range obj { + decoded, err := decodeCapdataLegacyValue(v, slots, remotables, transformations) + if err != nil { + return nil, err + } + obj[k] = decoded + } + return obj, nil + } else { + return encoded, nil + } +} + +// decodeCapdataSmallcapsValue decodes the "smallcaps" encoding from +// https://github.com/endojs/endo/blob/master/packages/marshal/src/encodeToSmallcaps.js +func decodeCapdataSmallcapsValue( + encoded interface{}, + slots []interface{}, + remotables map[uint64]*CapdataRemotable, + transformations CapdataValueTransformations, +) (interface{}, error) { + if arr, ok := encoded.([]interface{}); ok { + for i, v := range arr { + decoded, err := decodeCapdataSmallcapsValue(v, slots, remotables, transformations) + if err != nil { + return nil, err + } + arr[i] = decoded + } + return arr, nil + } else if encodedObj, ok := encoded.(map[string]interface{}); ok { + if _, ok := encodedObj["#tag"]; ok { + return nil, fmt.Errorf("not implemented: #tag") + } + if _, ok := encodedObj["#error"]; ok { + return nil, fmt.Errorf("not implemented: #error") + } + // We need a distinct output map to avoid reprocessing already-decoded keys. + decodedObj := make(map[string]interface{}, len(encodedObj)) + for encodedK, v := range encodedObj { + if strings.HasPrefix(encodedK, "#") { + return nil, fmt.Errorf("unrecognized record type: %q", encodedK) + } + decodedK, err := decodeCapdataSmallcapsValue(encodedK, slots, remotables, CapdataValueTransformations{}) + k, ok := decodedK.(string) + if err != nil || !ok { + return nil, fmt.Errorf("invalid copyRecord key: %q", encodedK) + } + decoded, err := decodeCapdataSmallcapsValue(v, slots, remotables, transformations) + if err != nil { + return nil, err + } + decodedObj[k] = decoded + } + return decodedObj, nil + } else if str, ok := encoded.(string); ok { + if len(str) == 0 { + return str, nil + } + switch str[0] { + case '!': + return str[1:], nil + case '+': + // Normalize to no leading "+". + str = str[1:] + fallthrough + case '-': + bigint := NewCapdataBigint(str) + if bigint == nil { + return nil, fmt.Errorf("invalid bigint: %q", encoded.(string)) + } + if transformations.Bigint == nil { + return nil, fmt.Errorf("untransformed bigint") + } + return transformations.Bigint(bigint), nil + case '$': + var slotIndexStr string + var iface *string + if dotIndex := strings.IndexByte(str, '.'); dotIndex >= 0 { + slotIndexStr = str[1:dotIndex] + ifaceStr := str[dotIndex+1:] + iface = &ifaceStr + } else { + slotIndexStr = str[1:] + } + slotIndex, err := strconv.ParseUint(slotIndexStr, 10, 0) + if err != nil || slotIndex >= uint64(len(slots)) { + return nil, fmt.Errorf("invalid slot index: %q", str) + } + r, err := upsertCapdataRemotable(remotables, slotIndex, slots[slotIndex], iface) + if err != nil { + return nil, fmt.Errorf("slot iface mismatch: %q", str) + } + return r, nil + case '#': + fallthrough + case '%': + fallthrough + case '&': + return nil, fmt.Errorf("not implemented: %q", str) + default: + if str[0] >= '!' && str[0] <= '-' { + return nil, fmt.Errorf("invalid smallcaps encoding prefix: %q", str[:1]) + } + } + return str, nil + } else { + return encoded, nil + } +} + +// DecodeSerializedCapdata accepts JSON text representing encoded CapData and +// decodes it, applying specified transformations for values that otherwise +// hinder interchange. +func DecodeSerializedCapdata( + serializedCapdata string, + transformations CapdataValueTransformations, +) (interface{}, error) { + var capdata Capdata + if err := json.Unmarshal([]byte(serializedCapdata), &capdata); err != nil { + return nil, err + } + if capdata.Body == "" || capdata.Slots == nil { + return nil, fmt.Errorf("invalid CapData") + } + serializedBody, decodeValue := capdata.Body, decodeCapdataLegacyValue + if strings.HasPrefix(serializedBody, "#") { + serializedBody, decodeValue = serializedBody[1:], decodeCapdataSmallcapsValue + } + var encoded interface{} + if err := json.Unmarshal([]byte(serializedBody), &encoded); err != nil { + return nil, err + } + remotables := map[uint64]*CapdataRemotable{} + decoded, err := decodeValue(encoded, capdata.Slots, remotables, transformations) + if err == nil && len(remotables) > 0 { + if transformations.Remotable == nil { + return nil, fmt.Errorf("untransformed remotable") + } + for _, r := range remotables { + r.Representation = transformations.Remotable(r) + } + } + return decoded, err +} From 8943f2f850e0cddb87a6fad0a36f01e4c350cf45 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 17 Jul 2023 12:43:25 -0400 Subject: [PATCH 02/11] feat(cosmos): Add a "CapData" vstorage RPC endpoint Fixes #7581 --- .../cosmos/proto/agoric/vstorage/query.proto | 54 ++++- golang/cosmos/x/vstorage/keeper/grpc_query.go | 201 ++++++++++++++++++ 2 files changed, 254 insertions(+), 1 deletion(-) diff --git a/golang/cosmos/proto/agoric/vstorage/query.proto b/golang/cosmos/proto/agoric/vstorage/query.proto index 23c0ed0f12c..a0850080232 100644 --- a/golang/cosmos/proto/agoric/vstorage/query.proto +++ b/golang/cosmos/proto/agoric/vstorage/query.proto @@ -9,11 +9,18 @@ option go_package = "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/types // Query defines the gRPC querier service service Query { - // Return an arbitrary vstorage datum. + // Return the raw string value of an arbitrary vstorage datum. rpc Data(QueryDataRequest) returns (QueryDataResponse) { option (google.api.http).get = "/agoric/vstorage/data/{path}"; } + // Return a formatted representation of a vstorage datum that must be + // a valid StreamCell with CapData values, or standalone CapData. + rpc CapData(QueryCapDataRequest) + returns (QueryCapDataResponse) { + option (google.api.http).get = "/agoric/vstorage/capdata/{path}"; + } + // Return the children of a given vstorage path. rpc Children(QueryChildrenRequest) returns (QueryChildrenResponse) { @@ -37,6 +44,51 @@ message QueryDataResponse { ]; } +// QueryCapDataRequest contains a path and formatting configuration. +message QueryCapDataRequest { + string path = 1 [ + (gogoproto.jsontag) = "path", + (gogoproto.moretags) = "yaml:\"path\"" + ]; + // mediaType must be an actual media type in the registry at + // https://www.iana.org/assignments/media-types/media-types.xhtml + // or a special value that does not conflict with the media type syntax. + // The only valid value is "JSON Lines", which is also the default. + string media_type = 2 [ + (gogoproto.jsontag) = "mediaType", + (gogoproto.moretags) = "yaml:\"mediaType\"" + ]; + // itemFormat, if present, must be the special value "flat" to indicate that + // the deep structure of each item should be flattened into a single level + // with kebab-case keys (e.g., `{ "metrics": { "min": 0, "max": 88 } }` as + // `{ "metrics-min": 0, "metrics-max": 88 }`). + string item_format = 3 [ + (gogoproto.jsontag) = "itemFormat", + (gogoproto.moretags) = "yaml:\"itemFormat\"" + ]; + // remotableValueFormat indicates how to transform references to opaque but + // distinguishable Remotables into readable embedded representations. + // * "object" represents each Remotable as an `{ id, allegedName }` object. + // * "string" represents each Remotable as a bracketed string such as `[Alleged: IST brand {}]`. + string remotable_value_format = 10 [ + (gogoproto.jsontag) = "remotableValueFormat", + (gogoproto.moretags) = "yaml:\"remotableValueFormat\"" + ]; +} + +// QueryCapDataResponse represents the result with the requested formatting, +// reserving space for future metadata such as media type. +message QueryCapDataResponse { + string block_height = 1 [ + (gogoproto.jsontag) = "blockHeight", + (gogoproto.moretags) = "yaml:\"blockHeight\"" + ]; + string value = 10 [ + (gogoproto.jsontag) = "value", + (gogoproto.moretags) = "yaml:\"value\"" + ]; +} + // QueryChildrenRequest is the vstorage path children query. message QueryChildrenRequest { string path = 1 [ diff --git a/golang/cosmos/x/vstorage/keeper/grpc_query.go b/golang/cosmos/x/vstorage/keeper/grpc_query.go index 2f365c7a243..6b23ec5bfb8 100644 --- a/golang/cosmos/x/vstorage/keeper/grpc_query.go +++ b/golang/cosmos/x/vstorage/keeper/grpc_query.go @@ -1,11 +1,15 @@ package keeper import ( + "encoding/json" + "fmt" "context" + "strings" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/capdata" "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -17,6 +21,10 @@ type Querier struct { var _ types.QueryServer = Querier{} +// =================================================================== +// /agoric.vstorage.Query/Data +// =================================================================== + func (k Querier) Data(c context.Context, req *types.QueryDataRequest) (*types.QueryDataResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") @@ -30,6 +38,199 @@ func (k Querier) Data(c context.Context, req *types.QueryDataRequest) (*types.Qu }, nil } +// =================================================================== +// /agoric.vstorage.Query/CapData +// =================================================================== + +const ( + // Media types. + JSONLines = "JSON Lines" + + // CapData transformation formats. + FormatCapDataFlat = "flat" + + // CapData remotable value formats. + FormatRemotableAsObject = "object" + FormatRemotableAsString = "string" +) + +var capDataResponseMediaTypes = map[string]string{ + JSONLines: JSONLines, + // Default to JSON Lines. + "": JSONLines, +} +var capDataTransformationFormats = map[string]string{ + FormatCapDataFlat: FormatCapDataFlat, + // Default to no transformation. + "": "", +} +var capDataRemotableValueFormats = map[string]string{ + FormatRemotableAsObject: FormatRemotableAsObject, + FormatRemotableAsString: FormatRemotableAsString, + // No default. +} + +// flatten converts data into a flat structure in which each deep leaf entry is replaced with +// a top-level entry having the same value but a key generated by joining the keys on its path +// with separating dashes. +// For example, +// ``` +// { "contacts": [ +// { "name": "Alice", "email": "a@example.com" }, +// { "name": "Bob", "email": "b@example.com" } +// ] } +// ``` +// becomes +// ``` +// { +// "contacts-0-name": "Alice", +// "contacts-0-email": "a@example.com", +// "contacts-1-name": "Bob", +// "contacts-1-email": "b@example.com" +// } +// ``` +// cf. https://github.com/Agoric/agoric-sdk/blob/6e5b422b80e47c4dac151404f43faea5ab41e9b0/scripts/get-flattened-publication.sh +func flatten(input interface{}, output map[string]interface{}, key string, top bool) error { + // Act on the raw representation of a Remotable. + if capdata, ok := input.(*capdata.CapdataRemotable); ok { + repr, _ := json.Marshal(capdata) + var replacement interface{} + json.Unmarshal(repr, &replacement) + input = replacement + } + + childKeyPrefix := key + if !top { + childKeyPrefix = childKeyPrefix + "-" + } + if arr, ok := input.([]interface{}); ok { + for i, v := range arr { + flatten(v, output, childKeyPrefix + fmt.Sprintf("%d", i), false) + } + } else if obj, ok := input.(map[string]interface{}); ok { + for k, v := range obj { + flatten(v, output, childKeyPrefix + k, false) + } + } else { + if _, has := output[key]; has { + return fmt.Errorf("key conflict: %q", key) + } + output[key] = input + } + return nil +} + +// capdataBigintToDigits represents a bigint as a string consisting of +// an optional "-" followed by a sequence of digits with no extraneous zeroes +// (e.g., "0" or "-40"). +func capdataBigintToDigits(bigint *capdata.CapdataBigint) interface{} { + return bigint.Normalized +} + +// capdataRemotableToString represents a Remotable as a bracketed string +// containing its alleged name (e.g., "[Foo {}]"). +func capdataRemotableToString(r *capdata.CapdataRemotable) interface{} { + iface := "Remotable" + if r.Iface != nil || *r.Iface != "" { + iface = *r.Iface + } + return fmt.Sprintf("[%s {}]", iface) +} + +// capdataRemotableToObject represents a Remotable as an object containing +// its id from `slots` and alleged name minus any "Alleged:" prefix or "brand" +// suffix (e.g., `{ "id": "board007", "allegedName": "IST" }`). +func capdataRemotableToObject(r *capdata.CapdataRemotable) interface{} { + iface := "Remotable" + if r.Iface != nil || *r.Iface != "" { + iface = *r.Iface + iface, _ = strings.CutPrefix(iface, "Alleged: ") + iface, _ = strings.CutSuffix(iface, " brand") + } + return map[string]interface{}{"id": r.Id, "allegedName": iface} +} + +func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*types.QueryCapDataResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + ctx := sdk.UnwrapSDKContext(c) + + valueTransformations := capdata.CapdataValueTransformations{ + Bigint: capdataBigintToDigits, + } + + // Read options. + mediaType, ok := capDataResponseMediaTypes[req.MediaType] + if !ok { + return nil, status.Error(codes.InvalidArgument, "invalid media_type") + } + transformation, ok := capDataTransformationFormats[req.ItemFormat] + if !ok { + return nil, status.Error(codes.InvalidArgument, "invalid item_format") + } + remotableFormat, ok := capDataRemotableValueFormats[req.RemotableValueFormat] + if !ok { + return nil, status.Error(codes.InvalidArgument, "invalid remotable_value_format") + } + switch remotableFormat { + case FormatRemotableAsObject: + valueTransformations.Remotable = capdataRemotableToObject + case FormatRemotableAsString: + valueTransformations.Remotable = capdataRemotableToString + } + + // Read data, auto-upgrading a standalone value to a single-value StreamCell. + entry := k.GetEntry(ctx, req.Path) + if !entry.HasData() { + return nil, status.Error(codes.FailedPrecondition, "no data") + } + value := entry.StringValue() + var cell StreamCell + _ = json.Unmarshal([]byte(value), &cell) + if cell.BlockHeight == "" { + cell = StreamCell{Values: []string{value}} + } + + // Format each StreamCell value. + responseItems := make([]string, len(cell.Values)) + for i, capDataJson := range cell.Values { + item, err := capdata.DecodeSerializedCapdata(capDataJson, valueTransformations) + if err != nil { + return nil, status.Error(codes.FailedPrecondition, err.Error()) + } + if transformation == FormatCapDataFlat { + flattened := map[string]interface{}{} + if err := flatten(item, flattened, "", true); err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + // Replace the item, unless it was a scalar that "flattened" to `{ "": ... }`. + if _, singleton := flattened[""]; !singleton { + item = flattened + } + } + switch mediaType { + case JSONLines: + transformedJson, err := json.Marshal(item) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + responseItems[i] = string(transformedJson) + default: + return nil, status.Error(codes.InvalidArgument, "invalid media_type") + } + } + + return &types.QueryCapDataResponse{ + BlockHeight: cell.BlockHeight, + Value: strings.Join(responseItems, "\n"), + }, nil +} + +// =================================================================== +// /agoric.vstorage.Query/Children +// =================================================================== + func (k Querier) Children(c context.Context, req *types.QueryChildrenRequest) (*types.QueryChildrenResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") From b68a5baa71fae02da0ad550f5831927d68968441 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 17 Jul 2023 12:47:28 -0400 Subject: [PATCH 03/11] chore(cosmos): Regenerate protobuf code --- golang/cosmos/x/swingset/types/msgs.pb.go | 32 +- golang/cosmos/x/vstorage/types/query.pb.go | 682 +++++++++++++++++- golang/cosmos/x/vstorage/types/query.pb.gw.go | 119 +++ 3 files changed, 781 insertions(+), 52 deletions(-) diff --git a/golang/cosmos/x/swingset/types/msgs.pb.go b/golang/cosmos/x/swingset/types/msgs.pb.go index 8f7016bc91c..14f437c6580 100644 --- a/golang/cosmos/x/swingset/types/msgs.pb.go +++ b/golang/cosmos/x/swingset/types/msgs.pb.go @@ -433,7 +433,7 @@ type MsgInstallBundle struct { Submitter github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,2,opt,name=submitter,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"submitter" yaml:"submitter"` // Either bundle or compressed_bundle will be set. // Default compression algorithm is gzip. - CompressedBundle []byte `protobuf:"bytes,3,opt,name=compressed_bundle,json=compressedBundle,proto3" json:"compressedBundle" yaml:"bcompressedBndle"` + CompressedBundle []byte `protobuf:"bytes,3,opt,name=compressed_bundle,json=compressedBundle,proto3" json:"compressedBundle" yaml:"compressedBundle"` // Size in bytes of uncompression of compressed_bundle. UncompressedSize int64 `protobuf:"varint,4,opt,name=uncompressed_size,json=uncompressedSize,proto3" json:"uncompressedSize"` } @@ -553,7 +553,7 @@ func init() { func init() { proto.RegisterFile("agoric/swingset/msgs.proto", fileDescriptor_788baa062b181a57) } var fileDescriptor_788baa062b181a57 = []byte{ - // 789 bytes of a gzipped FileDescriptorProto + // 788 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0xcf, 0x6f, 0xe3, 0x44, 0x14, 0x8e, 0xe3, 0x50, 0x36, 0xaf, 0xd9, 0x6d, 0x63, 0x95, 0xad, 0xd7, 0x0b, 0x99, 0xac, 0xa5, 0x15, 0x01, 0xd4, 0x44, 0xb0, 0xb7, 0xed, 0x29, 0x16, 0x42, 0x5a, 0xa4, 0xa0, 0xc5, 0x2b, 0x84, @@ -590,20 +590,20 @@ var fileDescriptor_788baa062b181a57 = []byte{ 0x01, 0x8d, 0x2d, 0xcf, 0x33, 0x66, 0xc1, 0xc4, 0xc3, 0xca, 0x0b, 0xd8, 0x1a, 0xf3, 0x7f, 0xf9, 0xe9, 0x3c, 0x4d, 0x18, 0xca, 0x91, 0x94, 0xa1, 0x87, 0xc2, 0x9e, 0x88, 0x75, 0x33, 0x27, 0x96, 0x57, 0x56, 0xbf, 0x87, 0x95, 0x29, 0xdf, 0x40, 0xdb, 0x26, 0x7e, 0x98, 0xc1, 0x78, 0x72, 0x9c, - 0x3b, 0x96, 0x79, 0xe7, 0x41, 0xc2, 0xd0, 0x6e, 0x45, 0x1a, 0x85, 0xf7, 0xfd, 0xdc, 0xfb, 0x02, - 0x25, 0x56, 0xb1, 0x26, 0x56, 0x86, 0xd0, 0x9e, 0x05, 0x0b, 0xf5, 0xa9, 0x7b, 0x89, 0xf9, 0x89, - 0xc9, 0xc6, 0x5e, 0x56, 0x7d, 0x91, 0x7c, 0xe3, 0x5e, 0x62, 0x73, 0x0d, 0xd1, 0x35, 0x50, 0x57, - 0xf7, 0xb6, 0xd8, 0xf8, 0x4f, 0xae, 0x65, 0x90, 0x47, 0xd4, 0x51, 0xbe, 0x85, 0x87, 0xcb, 0x9b, - 0xff, 0xac, 0xbf, 0xf2, 0x1a, 0xe8, 0xaf, 0xd6, 0xd0, 0x3e, 0xb8, 0x55, 0x52, 0xb4, 0x51, 0x4e, - 0xe0, 0xd1, 0xca, 0x8b, 0x42, 0xdf, 0x94, 0xbc, 0xac, 0xd1, 0x3e, 0xbc, 0x5d, 0x53, 0x76, 0x38, - 0x82, 0xd6, 0xd2, 0xc3, 0xb4, 0xbb, 0x29, 0x77, 0x51, 0xa1, 0xf5, 0x6e, 0x53, 0x94, 0xb5, 0x5d, - 0x68, 0xaf, 0x3f, 0xf9, 0x9e, 0xff, 0x73, 0xfa, 0x82, 0x4c, 0x3b, 0xf8, 0x4f, 0xb2, 0xb2, 0xd5, - 0x97, 0xd0, 0xac, 0x1e, 0x50, 0xef, 0x6d, 0xca, 0x2d, 0x69, 0xed, 0xf9, 0xbf, 0xd2, 0x45, 0x49, - 0xe3, 0xab, 0xdf, 0xe6, 0x1d, 0xe9, 0x6a, 0xde, 0x91, 0xae, 0xe7, 0x1d, 0xe9, 0xc7, 0x9b, 0x4e, - 0xed, 0xea, 0xa6, 0x53, 0xfb, 0xfd, 0xa6, 0x53, 0x3b, 0x3a, 0x5c, 0x98, 0xf9, 0xa1, 0xf8, 0x20, - 0x10, 0x15, 0xf9, 0xcc, 0x3b, 0xc4, 0xb3, 0x02, 0xa7, 0xb8, 0x0c, 0xdf, 0x57, 0xdf, 0x0a, 0xfc, - 0x32, 0x8c, 0xb7, 0xf8, 0x67, 0xc0, 0x8b, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x99, 0x11, 0x18, - 0xbe, 0x4b, 0x08, 0x00, 0x00, + 0x3b, 0x96, 0x79, 0xe7, 0x41, 0xc2, 0xd0, 0x6e, 0x45, 0x1a, 0x85, 0xf7, 0x7d, 0x61, 0x60, 0x95, + 0xd1, 0xcd, 0x35, 0xb1, 0x32, 0x84, 0xf6, 0x2c, 0x58, 0xa8, 0x4f, 0xdd, 0x4b, 0xcc, 0x4f, 0x4c, + 0x36, 0xf6, 0xb2, 0xea, 0x8b, 0xe4, 0x1b, 0xf7, 0x12, 0x9b, 0x6b, 0x88, 0xae, 0x81, 0xba, 0xba, + 0xb7, 0xc5, 0xc6, 0x7f, 0x72, 0x2d, 0x83, 0x3c, 0xa2, 0x8e, 0xf2, 0x2d, 0x3c, 0x5c, 0xde, 0xfc, + 0x67, 0xfd, 0x95, 0xd7, 0x40, 0x7f, 0xb5, 0x86, 0xf6, 0xc1, 0xad, 0x92, 0xa2, 0x8d, 0x72, 0x02, + 0x8f, 0x56, 0x5e, 0x14, 0xfa, 0xa6, 0xe4, 0x65, 0x8d, 0xf6, 0xe1, 0xed, 0x9a, 0xb2, 0xc3, 0x11, + 0xb4, 0x96, 0x1e, 0xa6, 0xdd, 0x4d, 0xb9, 0x8b, 0x0a, 0xad, 0x77, 0x9b, 0xa2, 0xac, 0xed, 0x42, + 0x7b, 0xfd, 0xc9, 0xf7, 0xfc, 0x9f, 0xd3, 0x17, 0x64, 0xda, 0xc1, 0x7f, 0x92, 0x95, 0xad, 0xbe, + 0x84, 0x66, 0xf5, 0x80, 0x7a, 0x6f, 0x53, 0x6e, 0x49, 0x6b, 0xcf, 0xff, 0x95, 0x2e, 0x4a, 0x1a, + 0x5f, 0xfd, 0x36, 0xef, 0x48, 0x57, 0xf3, 0x8e, 0x74, 0x3d, 0xef, 0x48, 0x3f, 0xde, 0x74, 0x6a, + 0x57, 0x37, 0x9d, 0xda, 0xef, 0x37, 0x9d, 0xda, 0xd1, 0xe1, 0xc2, 0xcc, 0x0f, 0xc5, 0x07, 0x81, + 0xa8, 0xc8, 0x67, 0xde, 0x21, 0x9e, 0x15, 0x38, 0xc5, 0x65, 0xf8, 0xbe, 0xfa, 0x56, 0xe0, 0x97, + 0x61, 0xbc, 0xc5, 0x3f, 0x03, 0x5e, 0xfc, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x51, 0x66, 0x1b, 0xd5, + 0x4b, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/golang/cosmos/x/vstorage/types/query.pb.go b/golang/cosmos/x/vstorage/types/query.pb.go index e1c6e586d53..b8a790ca63e 100644 --- a/golang/cosmos/x/vstorage/types/query.pb.go +++ b/golang/cosmos/x/vstorage/types/query.pb.go @@ -120,6 +120,141 @@ func (m *QueryDataResponse) GetValue() string { return "" } +// QueryCapDataRequest contains a path and formatting configuration. +type QueryCapDataRequest struct { + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path" yaml:"path"` + // mediaType must be an actual media type in the registry at + // https://www.iana.org/assignments/media-types/media-types.xhtml + // or a special value that does not conflict with the media type syntax. + // The only valid value is "JSON Lines", which is also the default. + MediaType string `protobuf:"bytes,2,opt,name=media_type,json=mediaType,proto3" json:"mediaType" yaml:"mediaType"` + // itemFormat, if present, must be the special value "flat" to indicate that + // the deep structure of each item should be flattened into a single level + // with kebab-case keys (e.g., `{ "metrics": { "min": 0, "max": 88 } }` as + // `{ "metrics-min": 0, "metrics-max": 88 }`). + ItemFormat string `protobuf:"bytes,3,opt,name=item_format,json=itemFormat,proto3" json:"itemFormat" yaml:"itemFormat"` + // remotableValueFormat indicates how to transform references to opaque but + // distinguishable Remotables into readable embedded representations. + // * "object" represents each Remotable as an `{ id, allegedName }` object. + // * "string" represents each Remotable as a bracketed string such as `[Alleged: IST brand {}]`. + RemotableValueFormat string `protobuf:"bytes,10,opt,name=remotable_value_format,json=remotableValueFormat,proto3" json:"remotableValueFormat" yaml:"remotableValueFormat"` +} + +func (m *QueryCapDataRequest) Reset() { *m = QueryCapDataRequest{} } +func (m *QueryCapDataRequest) String() string { return proto.CompactTextString(m) } +func (*QueryCapDataRequest) ProtoMessage() {} +func (*QueryCapDataRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_a26d6d1a170e94ae, []int{2} +} +func (m *QueryCapDataRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCapDataRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCapDataRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryCapDataRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCapDataRequest.Merge(m, src) +} +func (m *QueryCapDataRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryCapDataRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCapDataRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCapDataRequest proto.InternalMessageInfo + +func (m *QueryCapDataRequest) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *QueryCapDataRequest) GetMediaType() string { + if m != nil { + return m.MediaType + } + return "" +} + +func (m *QueryCapDataRequest) GetItemFormat() string { + if m != nil { + return m.ItemFormat + } + return "" +} + +func (m *QueryCapDataRequest) GetRemotableValueFormat() string { + if m != nil { + return m.RemotableValueFormat + } + return "" +} + +// QueryCapDataResponse represents the result with the requested formatting, +// reserving space for future metadata such as media type. +type QueryCapDataResponse struct { + BlockHeight string `protobuf:"bytes,1,opt,name=block_height,json=blockHeight,proto3" json:"blockHeight" yaml:"blockHeight"` + Value string `protobuf:"bytes,10,opt,name=value,proto3" json:"value" yaml:"value"` +} + +func (m *QueryCapDataResponse) Reset() { *m = QueryCapDataResponse{} } +func (m *QueryCapDataResponse) String() string { return proto.CompactTextString(m) } +func (*QueryCapDataResponse) ProtoMessage() {} +func (*QueryCapDataResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a26d6d1a170e94ae, []int{3} +} +func (m *QueryCapDataResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryCapDataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryCapDataResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryCapDataResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryCapDataResponse.Merge(m, src) +} +func (m *QueryCapDataResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryCapDataResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryCapDataResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryCapDataResponse proto.InternalMessageInfo + +func (m *QueryCapDataResponse) GetBlockHeight() string { + if m != nil { + return m.BlockHeight + } + return "" +} + +func (m *QueryCapDataResponse) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + // QueryChildrenRequest is the vstorage path children query. type QueryChildrenRequest struct { Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path" yaml:"path"` @@ -130,7 +265,7 @@ func (m *QueryChildrenRequest) Reset() { *m = QueryChildrenRequest{} } func (m *QueryChildrenRequest) String() string { return proto.CompactTextString(m) } func (*QueryChildrenRequest) ProtoMessage() {} func (*QueryChildrenRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_a26d6d1a170e94ae, []int{2} + return fileDescriptor_a26d6d1a170e94ae, []int{4} } func (m *QueryChildrenRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -183,7 +318,7 @@ func (m *QueryChildrenResponse) Reset() { *m = QueryChildrenResponse{} } func (m *QueryChildrenResponse) String() string { return proto.CompactTextString(m) } func (*QueryChildrenResponse) ProtoMessage() {} func (*QueryChildrenResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_a26d6d1a170e94ae, []int{3} + return fileDescriptor_a26d6d1a170e94ae, []int{5} } func (m *QueryChildrenResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -229,6 +364,8 @@ func (m *QueryChildrenResponse) GetPagination() *query.PageResponse { func init() { proto.RegisterType((*QueryDataRequest)(nil), "agoric.vstorage.QueryDataRequest") proto.RegisterType((*QueryDataResponse)(nil), "agoric.vstorage.QueryDataResponse") + proto.RegisterType((*QueryCapDataRequest)(nil), "agoric.vstorage.QueryCapDataRequest") + proto.RegisterType((*QueryCapDataResponse)(nil), "agoric.vstorage.QueryCapDataResponse") proto.RegisterType((*QueryChildrenRequest)(nil), "agoric.vstorage.QueryChildrenRequest") proto.RegisterType((*QueryChildrenResponse)(nil), "agoric.vstorage.QueryChildrenResponse") } @@ -236,38 +373,49 @@ func init() { func init() { proto.RegisterFile("agoric/vstorage/query.proto", fileDescriptor_a26d6d1a170e94ae) } var fileDescriptor_a26d6d1a170e94ae = []byte{ - // 481 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0xb1, 0x6f, 0x13, 0x3f, - 0x14, 0x8e, 0xf3, 0x6b, 0x7f, 0x6a, 0x5d, 0xa4, 0x82, 0x55, 0x44, 0x08, 0xd5, 0x5d, 0xb0, 0xa0, - 0x44, 0x20, 0x6c, 0xb5, 0x6c, 0x74, 0x40, 0x84, 0x0a, 0x56, 0x38, 0x89, 0x85, 0xed, 0x25, 0xb1, - 0x9c, 0x13, 0x97, 0xf3, 0xf5, 0xec, 0x44, 0x44, 0x88, 0xa5, 0x8c, 0x2c, 0x48, 0xcc, 0xfc, 0x3f, - 0x8c, 0x95, 0x58, 0x98, 0x4e, 0x28, 0x61, 0xca, 0x98, 0xbf, 0x00, 0x9d, 0xed, 0xb4, 0xd1, 0x51, - 0x51, 0x89, 0xed, 0xde, 0xf7, 0xbd, 0xf7, 0xbd, 0xef, 0x3e, 0xdb, 0xf8, 0x16, 0x48, 0x95, 0xc7, - 0x3d, 0x3e, 0xd6, 0x46, 0xe5, 0x20, 0x05, 0x3f, 0x1e, 0x89, 0x7c, 0xc2, 0xb2, 0x5c, 0x19, 0x45, - 0xb6, 0x1d, 0xc9, 0x96, 0x64, 0x73, 0x47, 0x2a, 0xa9, 0x2c, 0xc7, 0xcb, 0x2f, 0xd7, 0xd6, 0xbc, - 0xdf, 0x53, 0x7a, 0xa8, 0x34, 0xef, 0x82, 0xf6, 0xf3, 0x7c, 0xbc, 0xdf, 0x15, 0x06, 0xf6, 0x79, - 0x06, 0x32, 0x4e, 0xc1, 0xc4, 0x2a, 0xf5, 0xbd, 0xbb, 0x52, 0x29, 0x99, 0x08, 0x0e, 0x59, 0xcc, - 0x21, 0x4d, 0x95, 0xb1, 0xa4, 0x76, 0x2c, 0x7d, 0x82, 0xaf, 0xbe, 0x2a, 0xe7, 0x8f, 0xc0, 0x40, - 0x24, 0x8e, 0x47, 0x42, 0x1b, 0xf2, 0x00, 0xaf, 0x65, 0x60, 0x06, 0x0d, 0xd4, 0x42, 0xed, 0xcd, - 0xce, 0x8d, 0x79, 0x11, 0xda, 0x7a, 0x51, 0x84, 0x5b, 0x13, 0x18, 0x26, 0x8f, 0x69, 0x59, 0xd1, - 0xc8, 0x82, 0xf4, 0x08, 0x5f, 0x5b, 0x11, 0xd0, 0x99, 0x4a, 0xb5, 0x20, 0x1c, 0xaf, 0x8f, 0x21, - 0x19, 0x09, 0x2f, 0x71, 0x73, 0x5e, 0x84, 0x0e, 0x58, 0x14, 0xe1, 0x15, 0xa7, 0x61, 0x4b, 0x1a, - 0x39, 0x98, 0x7e, 0x42, 0x78, 0xc7, 0xca, 0x3c, 0x1b, 0xc4, 0x49, 0x3f, 0x17, 0xe9, 0xbf, 0x78, - 0x21, 0xcf, 0x31, 0x3e, 0xff, 0xfd, 0x46, 0xbd, 0x85, 0xda, 0x5b, 0x07, 0x7b, 0xcc, 0x65, 0xc5, - 0xca, 0xac, 0x98, 0xcb, 0xda, 0x67, 0xc5, 0x5e, 0x82, 0x14, 0x7e, 0x51, 0xb4, 0x32, 0x49, 0xbf, - 0x22, 0x7c, 0xbd, 0xe2, 0xc6, 0xff, 0xd8, 0x21, 0xde, 0xe8, 0x79, 0xac, 0x81, 0x5a, 0xff, 0xb5, - 0x37, 0x3b, 0xe1, 0xbc, 0x08, 0xcf, 0xb0, 0x45, 0x11, 0x6e, 0x3b, 0x5b, 0x4b, 0x84, 0x46, 0x67, - 0x24, 0x79, 0x71, 0x81, 0xbd, 0x7b, 0x97, 0xda, 0x73, 0x9b, 0x57, 0xfd, 0x1d, 0x9c, 0xd4, 0xf1, - 0xba, 0xf5, 0x47, 0x34, 0x5e, 0x2b, 0x83, 0x27, 0xb7, 0x59, 0xe5, 0xe2, 0xb0, 0xea, 0xa9, 0x36, - 0xe9, 0xdf, 0x5a, 0xdc, 0x12, 0x7a, 0xe7, 0xe4, 0xfb, 0xaf, 0x2f, 0xf5, 0x80, 0xec, 0xf2, 0xea, - 0x25, 0xed, 0x83, 0x01, 0xfe, 0xbe, 0x4c, 0xf9, 0x03, 0xf9, 0x88, 0xf0, 0xc6, 0x32, 0x19, 0x72, - 0xf7, 0x62, 0xd9, 0xca, 0x39, 0x36, 0xf7, 0x2e, 0x6b, 0xf3, 0x0e, 0xda, 0xd6, 0x01, 0x25, 0xad, - 0x3f, 0x1c, 0x2c, 0x63, 0xf4, 0x2e, 0x3a, 0xaf, 0xbf, 0x4d, 0x03, 0x74, 0x3a, 0x0d, 0xd0, 0xcf, - 0x69, 0x80, 0x3e, 0xcf, 0x82, 0xda, 0xe9, 0x2c, 0xa8, 0xfd, 0x98, 0x05, 0xb5, 0x37, 0x87, 0x32, - 0x36, 0x83, 0x51, 0x97, 0xf5, 0xd4, 0x90, 0x3f, 0x75, 0x2a, 0x4e, 0xec, 0xa1, 0xee, 0xbf, 0xe5, - 0x52, 0x25, 0x90, 0x4a, 0xee, 0x5f, 0xd0, 0xbb, 0xf3, 0x05, 0x66, 0x92, 0x09, 0xdd, 0xfd, 0xdf, - 0xbe, 0x8b, 0x47, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x1d, 0x3e, 0x9b, 0xa7, 0x03, 0x00, - 0x00, + // 669 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0xcf, 0x4f, 0xd4, 0x40, + 0x14, 0xa6, 0xfc, 0x50, 0x98, 0x25, 0x01, 0x46, 0xd4, 0x75, 0x21, 0x1d, 0x18, 0xf9, 0x15, 0x8d, + 0x9d, 0x80, 0x07, 0x13, 0x39, 0xa8, 0x48, 0x90, 0xa3, 0x36, 0xea, 0xc1, 0xcb, 0x66, 0x76, 0x77, + 0xec, 0x36, 0xb4, 0x9d, 0xd2, 0xce, 0x12, 0x37, 0xc6, 0x98, 0xc8, 0xd1, 0x8b, 0xc6, 0xb3, 0x7f, + 0x88, 0xff, 0x81, 0x47, 0x12, 0x2f, 0x9e, 0x1a, 0x03, 0x9e, 0x7a, 0xdc, 0xbf, 0xc0, 0x74, 0x66, + 0xb6, 0x5b, 0x96, 0x55, 0x0c, 0xb7, 0xce, 0xf7, 0xbe, 0xf7, 0xbd, 0xaf, 0xf3, 0xde, 0x3c, 0x30, + 0x47, 0x1d, 0x1e, 0xb9, 0x75, 0x72, 0x10, 0x0b, 0x1e, 0x51, 0x87, 0x91, 0xfd, 0x16, 0x8b, 0xda, + 0x56, 0x18, 0x71, 0xc1, 0xe1, 0x94, 0x0a, 0x5a, 0xdd, 0x60, 0x65, 0xd6, 0xe1, 0x0e, 0x97, 0x31, + 0x92, 0x7d, 0x29, 0x5a, 0xe5, 0x56, 0x9d, 0xc7, 0x3e, 0x8f, 0x49, 0x8d, 0xc6, 0x3a, 0x9f, 0x1c, + 0xac, 0xd7, 0x98, 0xa0, 0xeb, 0x24, 0xa4, 0x8e, 0x1b, 0x50, 0xe1, 0xf2, 0x40, 0x73, 0xe7, 0x1d, + 0xce, 0x1d, 0x8f, 0x11, 0x1a, 0xba, 0x84, 0x06, 0x01, 0x17, 0x32, 0x18, 0xab, 0x28, 0x7e, 0x00, + 0xa6, 0x9f, 0x65, 0xf9, 0xdb, 0x54, 0x50, 0x9b, 0xed, 0xb7, 0x58, 0x2c, 0xe0, 0x6d, 0x30, 0x1a, + 0x52, 0xd1, 0x2c, 0x1b, 0x0b, 0xc6, 0xda, 0xc4, 0xd6, 0xf5, 0x34, 0x41, 0xf2, 0xdc, 0x49, 0x50, + 0xa9, 0x4d, 0x7d, 0xef, 0x3e, 0xce, 0x4e, 0xd8, 0x96, 0x20, 0xde, 0x06, 0x33, 0x05, 0x81, 0x38, + 0xe4, 0x41, 0xcc, 0x20, 0x01, 0x63, 0x07, 0xd4, 0x6b, 0x31, 0x2d, 0x71, 0x23, 0x4d, 0x90, 0x02, + 0x3a, 0x09, 0x9a, 0x54, 0x1a, 0xf2, 0x88, 0x6d, 0x05, 0xe3, 0x6f, 0xc3, 0xe0, 0x8a, 0x94, 0x79, + 0x4c, 0xc3, 0x8b, 0x5a, 0x81, 0x0f, 0x01, 0xf0, 0x59, 0xc3, 0xa5, 0x55, 0xd1, 0x0e, 0x59, 0x79, + 0x58, 0xa6, 0x2c, 0xa6, 0x09, 0x9a, 0x90, 0xe8, 0xf3, 0x76, 0x98, 0x95, 0x9f, 0x56, 0x79, 0x39, + 0x84, 0xed, 0x5e, 0x18, 0x6e, 0x83, 0x92, 0x2b, 0x98, 0x5f, 0x7d, 0xcd, 0x23, 0x9f, 0x8a, 0xf2, + 0x88, 0x94, 0xb8, 0x99, 0x26, 0x08, 0x64, 0xf0, 0x8e, 0x44, 0x3b, 0x09, 0x9a, 0x51, 0x1a, 0x3d, + 0x0c, 0xdb, 0x05, 0x02, 0xf4, 0xc1, 0xb5, 0x88, 0xf9, 0x5c, 0xd0, 0x9a, 0xc7, 0xaa, 0xf2, 0xff, + 0xba, 0x82, 0x40, 0x0a, 0xde, 0x4b, 0x13, 0x34, 0x9b, 0x33, 0x5e, 0x66, 0x84, 0x5c, 0x7a, 0x4e, + 0x49, 0x0f, 0x8a, 0x62, 0x7b, 0x60, 0x12, 0xfe, 0x6c, 0x80, 0xd9, 0xd3, 0x77, 0xa7, 0xbb, 0xb0, + 0x0b, 0x26, 0x6b, 0x1e, 0xaf, 0xef, 0x55, 0x9b, 0xcc, 0x75, 0x9a, 0x42, 0x5f, 0xe2, 0x72, 0x9a, + 0xa0, 0x92, 0xc4, 0x77, 0x25, 0xdc, 0x49, 0x10, 0x54, 0x45, 0x0b, 0x20, 0xb6, 0x8b, 0x94, 0x5e, + 0x3f, 0xc1, 0x7f, 0xf6, 0xf3, 0x63, 0xee, 0xa9, 0xe9, 0x7a, 0x8d, 0x88, 0x05, 0x17, 0x6a, 0xe8, + 0x0e, 0x00, 0xbd, 0x71, 0x96, 0x0d, 0x2d, 0x6d, 0xac, 0x58, 0x6a, 0xf6, 0xad, 0x6c, 0xf6, 0x2d, + 0xf5, 0x76, 0xf4, 0xec, 0x5b, 0x4f, 0xa9, 0xc3, 0x74, 0x21, 0xbb, 0x90, 0x89, 0xbf, 0x1a, 0xe0, + 0x6a, 0x9f, 0x1b, 0x7d, 0x45, 0x9b, 0x60, 0xbc, 0xae, 0xb1, 0xb2, 0xb1, 0x30, 0xb2, 0x36, 0xb1, + 0x85, 0xd2, 0x04, 0xe5, 0x58, 0x27, 0x41, 0x53, 0xca, 0x56, 0x17, 0xc1, 0x76, 0x1e, 0x84, 0x4f, + 0x06, 0xd8, 0x5b, 0x3d, 0xd7, 0x9e, 0xaa, 0x5c, 0xf4, 0xb7, 0x71, 0x38, 0x02, 0xc6, 0xa4, 0x3f, + 0x18, 0x83, 0xd1, 0xac, 0x85, 0x70, 0xd1, 0xea, 0x5b, 0x04, 0x56, 0xff, 0x2b, 0xad, 0xe0, 0x7f, + 0x51, 0x54, 0x11, 0xbc, 0xf4, 0xe1, 0xc7, 0xef, 0x2f, 0xc3, 0x26, 0x9c, 0x27, 0xfd, 0x4b, 0xa7, + 0x41, 0x05, 0x25, 0x6f, 0xb3, 0x5b, 0x7e, 0x07, 0xdf, 0x83, 0xcb, 0x7a, 0x74, 0xe0, 0xd2, 0x60, + 0xd1, 0xd3, 0xaf, 0xb2, 0xb2, 0x7c, 0x0e, 0x4b, 0x57, 0x5f, 0x95, 0xd5, 0x17, 0x21, 0x3a, 0x53, + 0xbd, 0x4e, 0xc3, 0xa2, 0x81, 0x43, 0x03, 0x8c, 0x77, 0x5b, 0x03, 0xff, 0x26, 0x7e, 0x7a, 0x90, + 0x2a, 0x2b, 0xe7, 0xd1, 0xb4, 0x89, 0x35, 0x69, 0x02, 0xc3, 0x85, 0xb3, 0x26, 0x34, 0x55, 0xbb, + 0xd8, 0x7a, 0xf1, 0xfd, 0xd8, 0x34, 0x8e, 0x8e, 0x4d, 0xe3, 0xd7, 0xb1, 0x69, 0x7c, 0x3a, 0x31, + 0x87, 0x8e, 0x4e, 0xcc, 0xa1, 0x9f, 0x27, 0xe6, 0xd0, 0xab, 0x4d, 0xc7, 0x15, 0xcd, 0x56, 0xcd, + 0xaa, 0x73, 0x9f, 0x3c, 0x52, 0x2a, 0x4a, 0xec, 0x4e, 0xdc, 0xd8, 0x23, 0x0e, 0xf7, 0x68, 0xe0, + 0x10, 0xbd, 0x92, 0xdf, 0xf4, 0x0a, 0x64, 0x6b, 0x28, 0xae, 0x5d, 0x92, 0x8b, 0xf6, 0xee, 0x9f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xf3, 0x08, 0x4f, 0xc5, 0xf8, 0x05, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -282,8 +430,11 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type QueryClient interface { - // Return an arbitrary vstorage datum. + // Return the raw string value of an arbitrary vstorage datum. Data(ctx context.Context, in *QueryDataRequest, opts ...grpc.CallOption) (*QueryDataResponse, error) + // Return a formatted representation of a vstorage datum that must be + // a valid StreamCell with CapData values, or standalone CapData. + CapData(ctx context.Context, in *QueryCapDataRequest, opts ...grpc.CallOption) (*QueryCapDataResponse, error) // Return the children of a given vstorage path. Children(ctx context.Context, in *QueryChildrenRequest, opts ...grpc.CallOption) (*QueryChildrenResponse, error) } @@ -305,6 +456,15 @@ func (c *queryClient) Data(ctx context.Context, in *QueryDataRequest, opts ...gr return out, nil } +func (c *queryClient) CapData(ctx context.Context, in *QueryCapDataRequest, opts ...grpc.CallOption) (*QueryCapDataResponse, error) { + out := new(QueryCapDataResponse) + err := c.cc.Invoke(ctx, "/agoric.vstorage.Query/CapData", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) Children(ctx context.Context, in *QueryChildrenRequest, opts ...grpc.CallOption) (*QueryChildrenResponse, error) { out := new(QueryChildrenResponse) err := c.cc.Invoke(ctx, "/agoric.vstorage.Query/Children", in, out, opts...) @@ -316,8 +476,11 @@ func (c *queryClient) Children(ctx context.Context, in *QueryChildrenRequest, op // QueryServer is the server API for Query service. type QueryServer interface { - // Return an arbitrary vstorage datum. + // Return the raw string value of an arbitrary vstorage datum. Data(context.Context, *QueryDataRequest) (*QueryDataResponse, error) + // Return a formatted representation of a vstorage datum that must be + // a valid StreamCell with CapData values, or standalone CapData. + CapData(context.Context, *QueryCapDataRequest) (*QueryCapDataResponse, error) // Return the children of a given vstorage path. Children(context.Context, *QueryChildrenRequest) (*QueryChildrenResponse, error) } @@ -329,6 +492,9 @@ type UnimplementedQueryServer struct { func (*UnimplementedQueryServer) Data(ctx context.Context, req *QueryDataRequest) (*QueryDataResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Data not implemented") } +func (*UnimplementedQueryServer) CapData(ctx context.Context, req *QueryCapDataRequest) (*QueryCapDataResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CapData not implemented") +} func (*UnimplementedQueryServer) Children(ctx context.Context, req *QueryChildrenRequest) (*QueryChildrenResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Children not implemented") } @@ -355,6 +521,24 @@ func _Query_Data_Handler(srv interface{}, ctx context.Context, dec func(interfac return interceptor(ctx, in, info, handler) } +func _Query_CapData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryCapDataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).CapData(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/agoric.vstorage.Query/CapData", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).CapData(ctx, req.(*QueryCapDataRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_Children_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryChildrenRequest) if err := dec(in); err != nil { @@ -381,6 +565,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "Data", Handler: _Query_Data_Handler, }, + { + MethodName: "CapData", + Handler: _Query_CapData_Handler, + }, { MethodName: "Children", Handler: _Query_Children_Handler, @@ -450,6 +638,94 @@ func (m *QueryDataResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *QueryCapDataRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryCapDataRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCapDataRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RemotableValueFormat) > 0 { + i -= len(m.RemotableValueFormat) + copy(dAtA[i:], m.RemotableValueFormat) + i = encodeVarintQuery(dAtA, i, uint64(len(m.RemotableValueFormat))) + i-- + dAtA[i] = 0x52 + } + if len(m.ItemFormat) > 0 { + i -= len(m.ItemFormat) + copy(dAtA[i:], m.ItemFormat) + i = encodeVarintQuery(dAtA, i, uint64(len(m.ItemFormat))) + i-- + dAtA[i] = 0x1a + } + if len(m.MediaType) > 0 { + i -= len(m.MediaType) + copy(dAtA[i:], m.MediaType) + i = encodeVarintQuery(dAtA, i, uint64(len(m.MediaType))) + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryCapDataResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryCapDataResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryCapDataResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x52 + } + if len(m.BlockHeight) > 0 { + i -= len(m.BlockHeight) + copy(dAtA[i:], m.BlockHeight) + i = encodeVarintQuery(dAtA, i, uint64(len(m.BlockHeight))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *QueryChildrenRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -573,6 +849,48 @@ func (m *QueryDataResponse) Size() (n int) { return n } +func (m *QueryCapDataRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Path) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.MediaType) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.ItemFormat) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.RemotableValueFormat) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryCapDataResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.BlockHeight) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func (m *QueryChildrenRequest) Size() (n int) { if m == nil { return 0 @@ -779,6 +1097,298 @@ func (m *QueryDataResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryCapDataRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryCapDataRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCapDataRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Path = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MediaType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MediaType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ItemFormat", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ItemFormat = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemotableValueFormat", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemotableValueFormat = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryCapDataResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryCapDataResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryCapDataResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BlockHeight = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryChildrenRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/golang/cosmos/x/vstorage/types/query.pb.gw.go b/golang/cosmos/x/vstorage/types/query.pb.gw.go index 568dcba2674..31dc3a070b1 100644 --- a/golang/cosmos/x/vstorage/types/query.pb.gw.go +++ b/golang/cosmos/x/vstorage/types/query.pb.gw.go @@ -87,6 +87,78 @@ func local_request_Query_Data_0(ctx context.Context, marshaler runtime.Marshaler } +var ( + filter_Query_CapData_0 = &utilities.DoubleArray{Encoding: map[string]int{"path": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_Query_CapData_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCapDataRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["path"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "path") + } + + protoReq.Path, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "path", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CapData_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.CapData(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_CapData_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryCapDataRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["path"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "path") + } + + protoReq.Path, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "path", err) + } + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CapData_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.CapData(ctx, &protoReq) + return msg, metadata, err + +} + var ( filter_Query_Children_0 = &utilities.DoubleArray{Encoding: map[string]int{"path": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} ) @@ -188,6 +260,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_CapData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_CapData_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_CapData_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_Children_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -272,6 +367,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_CapData_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_CapData_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_CapData_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_Children_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -298,11 +413,15 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie var ( pattern_Query_Data_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"agoric", "vstorage", "data", "path"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_CapData_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"agoric", "vstorage", "capdata", "path"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_Children_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"agoric", "vstorage", "children", "path"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( forward_Query_Data_0 = runtime.ForwardResponseMessage + forward_Query_CapData_0 = runtime.ForwardResponseMessage + forward_Query_Children_0 = runtime.ForwardResponseMessage ) From 22c7d49e4e89e3de507e1cf79d9823705257d7b9 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 17 Jul 2023 13:19:27 -0400 Subject: [PATCH 04/11] fixup! feat(cosmos): Add a "CapData" vstorage RPC endpoint chore(cosmos): Fix Go formatting --- golang/cosmos/x/vstorage/keeper/grpc_query.go | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/golang/cosmos/x/vstorage/keeper/grpc_query.go b/golang/cosmos/x/vstorage/keeper/grpc_query.go index 6b23ec5bfb8..3e7e5f06cb7 100644 --- a/golang/cosmos/x/vstorage/keeper/grpc_query.go +++ b/golang/cosmos/x/vstorage/keeper/grpc_query.go @@ -1,9 +1,9 @@ package keeper import ( + "context" "encoding/json" "fmt" - "context" "strings" "google.golang.org/grpc/codes" @@ -57,7 +57,7 @@ const ( var capDataResponseMediaTypes = map[string]string{ JSONLines: JSONLines, // Default to JSON Lines. - "": JSONLines, + "": JSONLines, } var capDataTransformationFormats = map[string]string{ FormatCapDataFlat: FormatCapDataFlat, @@ -76,18 +76,22 @@ var capDataRemotableValueFormats = map[string]string{ // For example, // ``` // { "contacts": [ -// { "name": "Alice", "email": "a@example.com" }, -// { "name": "Bob", "email": "b@example.com" } +// +// { "name": "Alice", "email": "a@example.com" }, +// { "name": "Bob", "email": "b@example.com" } +// // ] } // ``` // becomes // ``` -// { -// "contacts-0-name": "Alice", -// "contacts-0-email": "a@example.com", -// "contacts-1-name": "Bob", -// "contacts-1-email": "b@example.com" -// } +// +// { +// "contacts-0-name": "Alice", +// "contacts-0-email": "a@example.com", +// "contacts-1-name": "Bob", +// "contacts-1-email": "b@example.com" +// } +// // ``` // cf. https://github.com/Agoric/agoric-sdk/blob/6e5b422b80e47c4dac151404f43faea5ab41e9b0/scripts/get-flattened-publication.sh func flatten(input interface{}, output map[string]interface{}, key string, top bool) error { @@ -95,7 +99,9 @@ func flatten(input interface{}, output map[string]interface{}, key string, top b if capdata, ok := input.(*capdata.CapdataRemotable); ok { repr, _ := json.Marshal(capdata) var replacement interface{} - json.Unmarshal(repr, &replacement) + if err := json.Unmarshal(repr, &replacement); err != nil { + return err + } input = replacement } @@ -105,11 +111,15 @@ func flatten(input interface{}, output map[string]interface{}, key string, top b } if arr, ok := input.([]interface{}); ok { for i, v := range arr { - flatten(v, output, childKeyPrefix + fmt.Sprintf("%d", i), false) + if err := flatten(v, output, childKeyPrefix+fmt.Sprintf("%d", i), false); err != nil { + return err + } } } else if obj, ok := input.(map[string]interface{}); ok { for k, v := range obj { - flatten(v, output, childKeyPrefix + k, false) + if err := flatten(v, output, childKeyPrefix+k, false); err != nil { + return err + } } } else { if _, has := output[key]; has { @@ -157,7 +167,7 @@ func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*ty ctx := sdk.UnwrapSDKContext(c) valueTransformations := capdata.CapdataValueTransformations{ - Bigint: capdataBigintToDigits, + Bigint: capdataBigintToDigits, } // Read options. @@ -223,7 +233,7 @@ func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*ty return &types.QueryCapDataResponse{ BlockHeight: cell.BlockHeight, - Value: strings.Join(responseItems, "\n"), + Value: strings.Join(responseItems, "\n"), }, nil } From 9356509751063790072e34e8625d2b514f505ccc Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Fri, 21 Jul 2023 15:47:06 -0400 Subject: [PATCH 05/11] chore(cosmos): Improve vstorage capdata error messages --- golang/cosmos/x/vstorage/capdata/capdata.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/golang/cosmos/x/vstorage/capdata/capdata.go b/golang/cosmos/x/vstorage/capdata/capdata.go index 3fa1c7be7a1..533902c00bd 100644 --- a/golang/cosmos/x/vstorage/capdata/capdata.go +++ b/golang/cosmos/x/vstorage/capdata/capdata.go @@ -113,7 +113,7 @@ func decodeCapdataLegacyValue( if ifaceStr, ok := ifaceVal.(string); ok { iface = &ifaceStr } else if ifaceVal != nil { - return nil, fmt.Errorf("invalid iface: %q", ifaceVal) + return nil, fmt.Errorf("invalid slot iface: %q", ifaceVal) } return upsertCapdataRemotable(remotables, slotIndex, slots[slotIndex], iface) case "hilbert": @@ -133,7 +133,7 @@ func decodeCapdataLegacyValue( case "-Infinity": return nil, fmt.Errorf("not implemented: @qclass %q", qclass) default: - return nil, fmt.Errorf("invalid @qclass: %q", qclass) + return nil, fmt.Errorf("unrecognized @qclass: %q", qclass) } } for k, v := range obj { From 36b921d589c8b84fa4ff5fa4a294eb49a4c4f533 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Fri, 21 Jul 2023 15:47:46 -0400 Subject: [PATCH 06/11] test(cosmos): Test vstorage capdata --- .../cosmos/x/vstorage/capdata/capdata_test.go | 323 ++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 golang/cosmos/x/vstorage/capdata/capdata_test.go diff --git a/golang/cosmos/x/vstorage/capdata/capdata_test.go b/golang/cosmos/x/vstorage/capdata/capdata_test.go new file mode 100644 index 00000000000..58e7179889b --- /dev/null +++ b/golang/cosmos/x/vstorage/capdata/capdata_test.go @@ -0,0 +1,323 @@ +package capdata + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "testing" +) + +func ptr[T any](v T) *T { + return &v +} + +func mustMarshal(val any) string { + jsonText, err := json.Marshal(val) + if err != nil { + panic(err) + } + return string(jsonText) +} + +func mustUnmarshalString(jsonText string, ptr any) { + if err := json.Unmarshal([]byte(jsonText), ptr); err != nil { + panic(err) + } +} + +func prefixBigint(bigint *CapdataBigint) interface{} { + return fmt.Sprintf("bigint:%s", bigint.Normalized) +} + +func remotableToString(r *CapdataRemotable) interface{} { + iface := "" + if r.Iface != nil { + iface = *r.Iface + } + return fmt.Sprintf("remotable:%s{%s}", iface, r.Id) +} + +func Test_DecodeSerializedCapdata(t *testing.T) { + type testCase struct { + format string + label string + body string + slots []interface{} + expected string + errContains *string + transformations CapdataValueTransformations + } + testCases := []testCase{ + // JSON + // cf. https://github.com/endojs/endo/blob/209b612e0a267239f33cd607e94ccd179d3ba248/packages/marshal/test/test-marshal-capdata.js#L11 + {body: `[1, 2]`}, + {body: `{ "foo": 1 }`}, + {body: `{}`}, + {body: `{ "a": 1, "b": 2 }`}, + {body: `{ "a": 1, "b": { "c": 3 } }`}, + {body: `true`}, + {body: `1`}, + {body: `"abc"`}, + {body: `null`}, + + // transformation of non-JSON values + {format: "smallcaps", label: "bigint", + body: `"+98765432101234567890"`, + expected: `"bigint:98765432101234567890"`, + transformations: CapdataValueTransformations{Bigint: prefixBigint}, + }, + {format: "legacy", label: "bigint", + body: `{ "@qclass": "bigint", "digits": "98765432101234567890" }`, + expected: `"bigint:98765432101234567890"`, + transformations: CapdataValueTransformations{Bigint: prefixBigint}, + }, + {format: "smallcaps", label: "remotables", + body: `["$0.Foo", "$0"]`, + slots: []interface{}{"a"}, + expected: `["remotable:Foo{a}","remotable:Foo{a}"]`, + transformations: CapdataValueTransformations{Remotable: remotableToString}, + }, + {format: "legacy", label: "remotables", + body: `[{"@qclass":"slot","index":0,"iface":"Foo"}, {"@qclass":"slot","index":0}]`, + slots: []interface{}{"a"}, + expected: `["remotable:Foo{a}","remotable:Foo{a}"]`, + transformations: CapdataValueTransformations{Remotable: remotableToString}, + }, + {format: "smallcaps", label: "escaped string", + body: `"!#escaped"`, + expected: `"#escaped"`, + }, + + // unimplemented + {format: "smallcaps", + body: `"#undefined"`, + errContains: ptr("not implemented"), + }, + {format: "legacy", label: "undefined", + body: `{"@qclass":"undefined"}`, + errContains: ptr("not implemented"), + }, + {format: "smallcaps", + body: `"#NaN"`, + errContains: ptr("not implemented"), + }, + {format: "legacy", label: "NaN", + body: `{"@qclass":"NaN"}`, + errContains: ptr("not implemented"), + }, + {format: "smallcaps", + body: `"#Infinity"`, + errContains: ptr("not implemented"), + }, + {format: "legacy", label: "Infinity", + body: `{"@qclass":"Infinity"}`, + errContains: ptr("not implemented"), + }, + {format: "smallcaps", + body: `"#-Infinity"`, + errContains: ptr("not implemented"), + }, + {format: "legacy", label: "-Infinity", + body: `{"@qclass":"-Infinity"}`, + errContains: ptr("not implemented"), + }, + {format: "smallcaps", label: "symbol", + body: `"%foo"`, + errContains: ptr("not implemented"), + }, + {format: "legacy", label: "symbol", + body: `{"@qclass":"symbol"}`, + errContains: ptr("not implemented"), + }, + {format: "smallcaps", label: "tagged", + body: `{"#tag":"foo","payload":"bar"}`, + errContains: ptr("not implemented: #tag"), + }, + {format: "legacy", label: "tagged", + body: `{"@qclass":"tagged","tag":"foo","payload":"bar"}`, + errContains: ptr("not implemented"), + }, + {format: "smallcaps", label: "error", + body: `{"#error":"","name":"Error"}`, + errContains: ptr("not implemented: #error"), + }, + {format: "legacy", label: "error", + body: `{"@qclass":"error","message":"foo","name":"bar"}`, + errContains: ptr("not implemented"), + }, + {format: "smallcaps", label: "promise", + body: `"&0"`, + slots: []interface{}{"a"}, + errContains: ptr("not implemented"), + }, + {format: "legacy", label: "error", + body: `{"@qclass":"hilbert","original":"foo"}`, + errContains: ptr("not implemented"), + }, + + // missing transformations + {format: "smallcaps", label: "untransformed bigint", + body: `"+98765432101234567890"`, + errContains: ptr("untransformed bigint"), + }, + {format: "legacy", label: "untransformed bigint", + body: `{ "@qclass": "bigint", "digits": "98765432101234567890" }`, + errContains: ptr("untransformed bigint"), + }, + {format: "smallcaps", label: "untransformed remotable", + body: `["$0.Foo", "$0"]`, + slots: []interface{}{"a"}, + errContains: ptr("untransformed remotable"), + }, + {format: "legacy", label: "untransformed remotable", + body: `[{"@qclass":"slot","index":0,"iface":"Foo"}, {"@qclass":"slot","index":0}]`, + slots: []interface{}{"a"}, + errContains: ptr("untransformed remotable"), + }, + + // invalid data + {format: "smallcaps", label: "iface mismatch", + body: `["$0.Foo", "$0."]`, + slots: []interface{}{"a"}, + errContains: ptr("iface mismatch"), + }, + {format: "legacy", label: "iface mismatch", + body: `[{"@qclass":"slot","index":0,"iface":"Foo"}, {"@qclass":"slot","index":0,"iface":""}]`, + slots: []interface{}{"a"}, + errContains: ptr("iface mismatch"), + }, + {format: "smallcaps", label: "invalid slot index (out of bounds)", + body: `"$0.Foo"`, + errContains: ptr("invalid slot index"), + }, + {format: "legacy", label: "invalid slot index (out of bounds)", + body: `{"@qclass":"slot","index":0}`, + errContains: ptr("invalid slot index"), + }, + {format: "smallcaps", label: "invalid slot index (bad format)", + body: `"$x.Foo"`, + slots: []interface{}{"a"}, + errContains: ptr("invalid slot index"), + }, + {format: "legacy", label: "invalid slot index (missing)", + body: `{"@qclass":"slot"}`, + slots: []interface{}{"a"}, + errContains: ptr("invalid slot index"), + }, + {format: "legacy", label: "invalid slot index (null)", + body: `{"@qclass":"slot","index":null}`, + slots: []interface{}{"a"}, + errContains: ptr("invalid slot index"), + }, + {format: "legacy", label: "invalid slot index (string)", + body: `{"@qclass":"slot","index":"0"}`, + slots: []interface{}{"a"}, + errContains: ptr("invalid slot index"), + }, + {format: "legacy", label: "invalid slot index (non-integer)", + body: `{"@qclass":"slot","index":0.1}`, + slots: []interface{}{"a"}, + errContains: ptr("invalid slot index"), + }, + {format: "legacy", label: "invalid slot index (negative)", + body: `{"@qclass":"slot","index":-1}`, + slots: []interface{}{"a"}, + errContains: ptr("invalid slot index"), + }, + {format: "legacy", label: "invalid slot iface (number)", + body: `{"@qclass":"slot","index":0,"iface":0}`, + slots: []interface{}{"a"}, + errContains: ptr("invalid slot iface"), + }, + {format: "smallcaps", label: "unrecognized record type", + body: `{"#foo":0}`, + errContains: ptr("unrecognized record type"), + }, + {format: "legacy", label: "invalid @qclass (null)", + body: `{"@qclass":null}`, + errContains: ptr("invalid @qclass"), + }, + {format: "legacy", label: "invalid @qclass (number)", + body: `{"@qclass":1}`, + errContains: ptr("invalid @qclass"), + }, + {format: "legacy", label: "invalid @qclass (number)", + body: `{"@qclass":1}`, + errContains: ptr("invalid @qclass"), + }, + {format: "legacy", label: "unrecognized @qclass", + body: `{"@qclass":"foo"}`, + errContains: ptr("unrecognized @qclass"), + }, + {format: "smallcaps", label: "invalid copyRecord key", + body: `{"+0":0}`, + errContains: ptr("invalid copyRecord key"), + }, + {format: "smallcaps", label: "invalid bigint (`--`)", + body: `"--"`, + errContains: ptr("invalid bigint"), + }, + {format: "smallcaps", label: "invalid bigint (`+0x`)", + body: `"+0x"`, + errContains: ptr("invalid bigint"), + }, + {format: "legacy", label: "invalid bigint (no digits)", + body: `{"@qclass":"bigint"}`, + errContains: ptr("invalid bigint"), + }, + {format: "legacy", label: "invalid bigint (null digits)", + body: `{"@qclass":"bigint","digits":null}`, + errContains: ptr("invalid bigint"), + }, + {format: "legacy", label: "invalid bigint (`7up`)", + body: `{"@qclass":"bigint","digits":"7up"}`, + errContains: ptr("invalid bigint"), + }, + } + + for _, desc := range testCases { + slots := desc.slots + if slots == nil { + slots = []interface{}{} + } + var expected interface{} + if desc.expected != "" { + mustUnmarshalString(desc.expected, &expected) + } else { + mustUnmarshalString(desc.body, &expected) + } + for _, format := range []string{"smallcaps", "legacy"} { + if desc.format != "" && desc.format != format { + continue + } + label := fmt.Sprintf("%s %s", format, desc.label) + if desc.label == "" { + label = fmt.Sprintf("%s %s", format, desc.body) + } + capdata := Capdata{desc.body, slots} + if format == "smallcaps" { + capdata.Body = "#" + capdata.Body + } + intermediate, err := DecodeSerializedCapdata(mustMarshal(capdata), desc.transformations) + // Replace each Remotable with its representation before comparing. + var got interface{} + mustUnmarshalString(mustMarshal(intermediate), &got) + if desc.errContains == nil { + if err != nil { + t.Errorf("%s: got unexpected error %v", label, err) + } else if !reflect.DeepEqual(got, expected) { + result, err := json.Marshal(got) + if err != nil { + panic(fmt.Errorf("%s: %v", label, err)) + } + t.Errorf("%s: wrong result: %s", label, result) + } + } else if err == nil { + t.Errorf("%s: got no error, want error %q", label, *desc.errContains) + } else if !strings.Contains(err.Error(), *desc.errContains) { + t.Errorf("%s: got error %v, want error %q", label, err, *desc.errContains) + } + } + } +} From a7f17febb4412684293ffd8da1782d3700a7f59e Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Sat, 22 Jul 2023 14:27:00 -0400 Subject: [PATCH 07/11] test(cosmos): Test gRPC endpoint "/agoric.vstorage.Query/CapData" --- .../cosmos/x/vstorage/capdata/capdata_test.go | 12 +- .../x/vstorage/keeper/keeper_grpc_test.go | 300 ++++++++++++++++++ 2 files changed, 306 insertions(+), 6 deletions(-) create mode 100644 golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go diff --git a/golang/cosmos/x/vstorage/capdata/capdata_test.go b/golang/cosmos/x/vstorage/capdata/capdata_test.go index 58e7179889b..0100ad79820 100644 --- a/golang/cosmos/x/vstorage/capdata/capdata_test.go +++ b/golang/cosmos/x/vstorage/capdata/capdata_test.go @@ -12,7 +12,7 @@ func ptr[T any](v T) *T { return &v } -func mustMarshal(val any) string { +func mustJsonMarshal(val any) string { jsonText, err := json.Marshal(val) if err != nil { panic(err) @@ -20,7 +20,7 @@ func mustMarshal(val any) string { return string(jsonText) } -func mustUnmarshalString(jsonText string, ptr any) { +func mustJsonUnmarshal(jsonText string, ptr any) { if err := json.Unmarshal([]byte(jsonText), ptr); err != nil { panic(err) } @@ -283,9 +283,9 @@ func Test_DecodeSerializedCapdata(t *testing.T) { } var expected interface{} if desc.expected != "" { - mustUnmarshalString(desc.expected, &expected) + mustJsonUnmarshal(desc.expected, &expected) } else { - mustUnmarshalString(desc.body, &expected) + mustJsonUnmarshal(desc.body, &expected) } for _, format := range []string{"smallcaps", "legacy"} { if desc.format != "" && desc.format != format { @@ -299,10 +299,10 @@ func Test_DecodeSerializedCapdata(t *testing.T) { if format == "smallcaps" { capdata.Body = "#" + capdata.Body } - intermediate, err := DecodeSerializedCapdata(mustMarshal(capdata), desc.transformations) + intermediate, err := DecodeSerializedCapdata(mustJsonMarshal(capdata), desc.transformations) // Replace each Remotable with its representation before comparing. var got interface{} - mustUnmarshalString(mustMarshal(intermediate), &got) + mustJsonUnmarshal(mustJsonMarshal(intermediate), &got) if desc.errContains == nil { if err != nil { t.Errorf("%s: got unexpected error %v", label, err) diff --git a/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go b/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go new file mode 100644 index 00000000000..84437c0fba9 --- /dev/null +++ b/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go @@ -0,0 +1,300 @@ +package keeper + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "testing" + + grpcCodes "google.golang.org/grpc/codes" + grpcStatus "google.golang.org/grpc/status" + + //"github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/capdata" + "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func ptr[T any](v T) *T { + return &v +} + +func mustJsonMarshal(val any) string { + jsonText, err := json.Marshal(val) + if err != nil { + panic(err) + } + return string(jsonText) +} + +func mustMarshalStreamCell(blockHeight string, values []string) string { + cell := map[string]any{ + "blockHeight": blockHeight, + "values": values, + } + return mustJsonMarshal(cell) +} + +func TestCapData(t *testing.T) { + testKit := makeTestKit() + ctx, keeper := testKit.ctx, testKit.vstorageKeeper + querier := Querier{keeper} + + type testCase struct { + label string + data *string + request types.QueryCapDataRequest + expected types.QueryCapDataResponse + errCode grpcCodes.Code + errContains *string + } + testCases := []testCase{} + + // Test simple cases (various kinds of bad data up to simple flat JSON-compatible CapData). + decodableSmallcaps := `{"body":"#true","slots":[]}` + decodableLegacy := `{"body":"true","slots":[]}` + testCases = append(testCases, []testCase{ + {label: "no data", + data: nil, + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + errCode: grpcCodes.FailedPrecondition, + errContains: ptr("no data"), + }, + {label: "zero-length data", + data: ptr(""), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + errCode: grpcCodes.FailedPrecondition, + errContains: ptr("JSON"), + }, + {label: "cell with empty string", + data: ptr(mustMarshalStreamCell("1", []string{decodableSmallcaps, "", decodableLegacy})), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + errCode: grpcCodes.FailedPrecondition, + errContains: ptr("JSON"), + }, + {label: "non-JSON data", + data: ptr("foo"), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + errCode: grpcCodes.FailedPrecondition, + errContains: ptr("invalid"), + }, + {label: "cell with non-JSON", + data: ptr(mustMarshalStreamCell("1", []string{decodableSmallcaps, "foo", decodableLegacy})), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + errCode: grpcCodes.FailedPrecondition, + errContains: ptr("invalid"), + }, + {label: "lone non-CapData", + data: ptr("{}"), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + errCode: grpcCodes.FailedPrecondition, + errContains: ptr("invalid CapData"), + }, + {label: "cell with non-CapData", + data: ptr(mustMarshalStreamCell("1", []string{decodableSmallcaps, "{}", decodableLegacy})), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + errCode: grpcCodes.FailedPrecondition, + errContains: ptr("invalid CapData"), + }, + {label: "lone smallcaps CapData", + data: ptr(decodableSmallcaps), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + expected: types.QueryCapDataResponse{Value: `true`}, + }, + {label: "cell with smallcaps CapData", + data: ptr(mustMarshalStreamCell("1", []string{decodableSmallcaps})), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + expected: types.QueryCapDataResponse{BlockHeight: "1", Value: `true`}, + }, + {label: "lone legacy CapData", + data: ptr(decodableLegacy), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + expected: types.QueryCapDataResponse{Value: `true`}, + }, + {label: "cell with legacy CapData", + data: ptr(mustMarshalStreamCell("1", []string{decodableLegacy})), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + expected: types.QueryCapDataResponse{BlockHeight: "1", Value: `true`}, + }, + }...) + + // Test option validation. + testCases = append(testCases, []testCase{ + {label: "explicit JSON Lines", + data: ptr(decodableSmallcaps), + request: types.QueryCapDataRequest{MediaType: "JSON Lines", RemotableValueFormat: "string"}, + expected: types.QueryCapDataResponse{Value: `true`}, + }, + {label: "invalid media type", + data: ptr(decodableSmallcaps), + request: types.QueryCapDataRequest{MediaType: "JSONLines", RemotableValueFormat: "string"}, + errCode: grpcCodes.InvalidArgument, + errContains: ptr("media_type"), + }, + {label: "invalid item format", + data: ptr(decodableSmallcaps), + request: types.QueryCapDataRequest{ItemFormat: "deep", RemotableValueFormat: "string"}, + errCode: grpcCodes.InvalidArgument, + errContains: ptr("item_format"), + }, + {label: "missing remotable value format", + data: ptr(decodableSmallcaps), + request: types.QueryCapDataRequest{}, + errCode: grpcCodes.InvalidArgument, + errContains: ptr("remotable_value_format"), + }, + {label: "invalid remotable value format", + data: ptr(decodableSmallcaps), + request: types.QueryCapDataRequest{RemotableValueFormat: "foo"}, + errCode: grpcCodes.InvalidArgument, + errContains: ptr("remotable_value_format"), + }, + }...) + + // Test formatting options against sufficiently complex CapData, + // deriving legacy encoding from smallcaps encoding to ensure equivalence + // and deriving expectations from marshalling to avoid spurious mismatches + // from Go's unpredictable field ordering (e.g., `{"a":0,"b":1}` vs. `{"b":1,"a":0}`). + slots := []any{"a"} + deepSmallcapsBody := `{"arr":[{"bigint":"+42","remotable":"$0.Foo","ref2":"$0"}]}` + deepLegacyBody := deepSmallcapsBody + legacyFromSmallcaps := [][2]string{ + [2]string{`"+42"`, `{"@qclass":"bigint","digits":"42"}`}, + [2]string{`"$0.Foo"`, `{"@qclass":"slot","index":0,"iface":"Foo"}`}, + [2]string{`"$0"`, `{"@qclass":"slot","index":0}`}, + } + for _, pair := range legacyFromSmallcaps { + deepLegacyBody = strings.Replace(deepLegacyBody, pair[0], pair[1], -1) + } + cell := mustMarshalStreamCell("1", []string{ + mustJsonMarshal(map[string]any{"body": "#" + deepSmallcapsBody, "slots": slots}), + mustJsonMarshal(map[string]any{"body": deepLegacyBody, "slots": slots}), + }) + mustMarshalTwoLines := func(val any) string { + line := mustJsonMarshal(val) + return fmt.Sprintf("%s\n%s", line, line) + } + testCases = append(testCases, testCase{label: "remotables as strings", + data: ptr(cell), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + expected: types.QueryCapDataResponse{ + BlockHeight: "1", + Value: mustMarshalTwoLines(map[string]any{ + "arr": []any{ + map[string]any{ + "bigint": "42", + "remotable": "[Foo {}]", + "ref2": "[Foo {}]", + }, + }, + }), + }, + }) + testCases = append(testCases, testCase{label: "remotables as strings, flat", + data: ptr(cell), + request: types.QueryCapDataRequest{ItemFormat: "flat", RemotableValueFormat: "string"}, + expected: types.QueryCapDataResponse{ + BlockHeight: "1", + Value: mustMarshalTwoLines(map[string]any{ + "arr-0-bigint": "42", + "arr-0-remotable": "[Foo {}]", + "arr-0-ref2": "[Foo {}]", + }), + }, + }) + testCases = append(testCases, testCase{label: "remotables as objects", + data: ptr(cell), + request: types.QueryCapDataRequest{RemotableValueFormat: "object"}, + expected: types.QueryCapDataResponse{ + BlockHeight: "1", + Value: mustMarshalTwoLines(map[string]any{ + "arr": []any{ + map[string]any{ + "bigint": "42", + "remotable": map[string]any{"id": "a", "allegedName": "Foo"}, + "ref2": map[string]any{"id": "a", "allegedName": "Foo"}, + }, + }, + }), + }, + }) + testCases = append(testCases, testCase{label: "remotables as objects, flat", + data: ptr(cell), + request: types.QueryCapDataRequest{ItemFormat: "flat", RemotableValueFormat: "object"}, + expected: types.QueryCapDataResponse{ + BlockHeight: "1", + Value: mustMarshalTwoLines(map[string]any{ + "arr-0-bigint": "42", + "arr-0-remotable-id": "a", + "arr-0-remotable-allegedName": "Foo", + "arr-0-ref2-id": "a", + "arr-0-ref2-allegedName": "Foo", + }), + }, + }) + + // Test errors from CapData that includes unsupported values. + expectNotImplemented := func(label, capdataBody string, slots []any) testCase { + if slots == nil { + slots = []any{} + } + serialized := mustJsonMarshal(map[string]any{ + "body": capdataBody, + "slots": slots, + }) + return testCase{ + label: label, + data: ptr(serialized), + request: types.QueryCapDataRequest{RemotableValueFormat: "string"}, + errCode: grpcCodes.FailedPrecondition, + errContains: ptr("not implemented"), + } + } + testCases = append(testCases, []testCase{ + expectNotImplemented("smallcaps undefined", `#"#undefined"`, nil), + expectNotImplemented("smallcaps NaN", `#"#NaN"`, nil), + expectNotImplemented("smallcaps infinity", `#"#Infinity"`, nil), + expectNotImplemented("smallcaps negative infinity", `#"#-Infinity"`, nil), + expectNotImplemented("smallcaps symbol", `#"%foo"`, nil), + expectNotImplemented("smallcaps promise", `#"&0"`, []any{"a"}), + expectNotImplemented("smallcaps tagged", `#{"#tag":"copySet","payload":[]}`, nil), + expectNotImplemented("smallcaps error", `#{"#error":"foo","name":"Error"}`, nil), + expectNotImplemented("legacy undefined", `{"@qclass":"undefined"}`, nil), + expectNotImplemented("legacy NaN", `{"@qclass":"NaN"}`, nil), + expectNotImplemented("legacy infinity", `{"@qclass":"Infinity"}`, nil), + expectNotImplemented("legacy negative infinity", `{"@qclass":"-Infinity"}`, nil), + expectNotImplemented("legacy symbol", `{"@qclass":"symbol","name":"foo"}`, nil), + expectNotImplemented("smallcaps tagged", `{"@qclass":"tagged","tag":"copySet","payload":[]}`, nil), + expectNotImplemented("smallcaps error", `{"@qclass":"error","message":"foo","name":"Error"}`, nil), + expectNotImplemented("smallcaps Hilbert Hotel", `{"@qclass":"hilbert","original":"foo"}`, nil), + }...) + for _, desc := range testCases { + desc.request.Path = "key" + if desc.data == nil { + keeper.SetStorage(ctx, types.NewStorageEntryWithNoData(desc.request.Path)) + } else { + keeper.SetStorage(ctx, types.NewStorageEntry(desc.request.Path, *desc.data)) + } + resp, err := querier.CapData(sdk.WrapSDKContext(ctx), &desc.request) + if desc.errCode == grpcCodes.OK { + if err != nil { + t.Errorf("%s: got unexpected error %v", desc.label, err) + } else if reflect.DeepEqual(resp, &desc.expected) { + continue + } + if resp.Value != desc.expected.Value { + lines := strings.Split(resp.Value, "\n") + t.Errorf("%s: wrong result value lines: %#q", desc.label, lines) + } else { + t.Errorf("%s: wrong result: %#v", desc.label, resp) + } + } else if err == nil { + t.Errorf("%s: got no error, want error %q", desc.label, *desc.errContains) + } else if code := grpcStatus.Code(err); code != desc.errCode { + t.Errorf("%s: got error code %q, want %q", desc.label, code, desc.errCode) + } else if desc.errContains != nil && !strings.Contains(err.Error(), *desc.errContains) { + t.Errorf("%s: got error %v, want error %q", desc.label, err, *desc.errContains) + } + } +} From e4bfad38ba83a6e621c9aacd8870fa16ea058e30 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Tue, 25 Jul 2023 23:28:38 -0400 Subject: [PATCH 08/11] chore(cosmos): Improve vstorage/keeper/grpc_query.go doc comments --- golang/cosmos/x/vstorage/keeper/grpc_query.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/golang/cosmos/x/vstorage/keeper/grpc_query.go b/golang/cosmos/x/vstorage/keeper/grpc_query.go index 3e7e5f06cb7..15356314632 100644 --- a/golang/cosmos/x/vstorage/keeper/grpc_query.go +++ b/golang/cosmos/x/vstorage/keeper/grpc_query.go @@ -25,6 +25,7 @@ var _ types.QueryServer = Querier{} // /agoric.vstorage.Query/Data // =================================================================== +// /agoric.vstorage.Query/Data returns data for a specified path. func (k Querier) Data(c context.Context, req *types.QueryDataRequest) (*types.QueryDataResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") @@ -67,7 +68,7 @@ var capDataTransformationFormats = map[string]string{ var capDataRemotableValueFormats = map[string]string{ FormatRemotableAsObject: FormatRemotableAsObject, FormatRemotableAsString: FormatRemotableAsString, - // No default. + // No default because both formats are lossy. } // flatten converts data into a flat structure in which each deep leaf entry is replaced with @@ -160,6 +161,9 @@ func capdataRemotableToObject(r *capdata.CapdataRemotable) interface{} { return map[string]interface{}{"id": r.Id, "allegedName": iface} } +// /agoric.vstorage.Query/CapData returns data for a specified path, +// interpreted as CapData in a StreamCell (auto-promoting isolated CapData +// into a single-item StreamCell) and transformed as specified. func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*types.QueryCapDataResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") @@ -241,6 +245,10 @@ func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*ty // /agoric.vstorage.Query/Children // =================================================================== +// /agoric.vstorage.Query/Children returns the list of path segments +// that exist immediately underneath a specified path, including +// those corresponding with "empty non-terminals" having children +// but no data of their own. func (k Querier) Children(c context.Context, req *types.QueryChildrenRequest) (*types.QueryChildrenResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") From dff7e9329a2067f7b3ad8c42f73c39767146de70 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Tue, 25 Jul 2023 23:33:03 -0400 Subject: [PATCH 09/11] chore(cosmos): Add vstorage README --- golang/cosmos/x/vstorage/README.md | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 golang/cosmos/x/vstorage/README.md diff --git a/golang/cosmos/x/vstorage/README.md b/golang/cosmos/x/vstorage/README.md new file mode 100644 index 00000000000..2198aeb246e --- /dev/null +++ b/golang/cosmos/x/vstorage/README.md @@ -0,0 +1,52 @@ +# Virtual Storage + +This module manages "[IAVL](https://github.com/cosmos/iavl)" chain storage data with a hierarchical keyspace in which each key is a "[path](./types/path_keys.go)" composed of zero or more dot-separated nonempty segments in a restricted alphabet. It exposes gRPC endpoints to arbitrary external clients for reading data, and internal read/write interfaces for use by SwingSet (which itself manages further subtree-scoped attenuation). + +## Internal Go interface + +[Keeper](./keeper/keeper.go) +* generic + * GetChildren + * GetEntry + * HasEntry + * HasStorage + * SetStorage[AndNotify] +* StreamCell-oriented (a StreamCell captures a block height and an array of values) + * AppendStorageValue[AndNotify] +* queue-oriented (a queue stores items at paths like "$prefix.$n", documenting + the n for the next item to be consumed at "$prefix.head" and the n for the next + next item to be pushed at "$prefix.tail" such that the queue is empty when both + head and tail store the same n) + * GetQueueLength + * PushQueueItem + +## Internal JSON interface + +This is used by the SwingSet "bridge". + +[Receive](./vstorage.go) with input `{ "method": "...", "args": [...] }` +* generic + * method "entries", args path + * method "get"/"has", args path + * method "set"/"setWithoutNotify", args [[path, value?], ...] + * method "children", args path + * method "values", args path (returns values for children in the same order as method "children") + * method "size", args path (returns the count of children) +* StreamCell-oriented + * method "append", args [[path, value?], ...] + +## External protobuf interface + +gRPC via [Querier](./keeper/keeper.go) +and CometBFT method "abci_query" with params `{ "path": "/agoric.vstorage.Query/...", "data": "" }` via the same +* /agoric.vstorage.Query/CapData +* /agoric.vstorage.Query/Children +* /agoric.vstorage.Query/Data + +## Arbitrary-response HTTP interface + +This depends upon appModule `LegacyQuerierHandler` functionality that is [removed from cosmos-sdk as of v0.47](https://github.com/cosmos/cosmos-sdk/blob/fa4d87ef7e6d87aaccc94c337ffd2fe90fcb7a9d/CHANGELOG.md#api-breaking-changes-3) + +[legacy querier](./keeper/querier.go) +* /custom/vstorage/children/$path +* /custom/vstorage/data/$path From b4fdcc9003ec3ae2a5ba491fac1bfe90befab942 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Thu, 27 Jul 2023 12:50:02 -0400 Subject: [PATCH 10/11] chore(cosmos): Avoid special treatment of HTML characters in vstorage JSON encoding --- golang/cosmos/x/vstorage/capdata/capdata.go | 17 +++++++++- .../cosmos/x/vstorage/capdata/capdata_test.go | 33 +++++++++++++++++-- golang/cosmos/x/vstorage/keeper/grpc_query.go | 15 +++++---- .../x/vstorage/keeper/keeper_grpc_test.go | 5 ++- 4 files changed, 57 insertions(+), 13 deletions(-) diff --git a/golang/cosmos/x/vstorage/capdata/capdata.go b/golang/cosmos/x/vstorage/capdata/capdata.go index 533902c00bd..57d60fa7b39 100644 --- a/golang/cosmos/x/vstorage/capdata/capdata.go +++ b/golang/cosmos/x/vstorage/capdata/capdata.go @@ -1,6 +1,7 @@ package capdata import ( + "bytes" "encoding/json" "fmt" "regexp" @@ -8,6 +9,20 @@ import ( "strings" ) +// JsonMarshal returns JSON text representing its input, +// without special replacement of "<", ">", "&", U+2028, or U+2029. +func JsonMarshal(val any) ([]byte, error) { + buf := &bytes.Buffer{} + encoder := json.NewEncoder(buf) + encoder.SetEscapeHTML(false) + if err := encoder.Encode(val); err != nil { + return nil, err + } + // Return without a trailing line feed. + lineTerminatedJson := buf.Bytes() + return bytes.TrimSuffix(lineTerminatedJson, []byte("\n")), nil +} + // cf. https://github.com/endojs/endo/tree/master/packages/marshal type Capdata struct { @@ -36,7 +51,7 @@ func NewCapdataBigint(str string) *CapdataBigint { } func (r *CapdataRemotable) MarshalJSON() ([]byte, error) { - return json.Marshal(r.Representation) + return JsonMarshal(r.Representation) } type CapdataValueTransformations struct { diff --git a/golang/cosmos/x/vstorage/capdata/capdata_test.go b/golang/cosmos/x/vstorage/capdata/capdata_test.go index 0100ad79820..8a83b90a95f 100644 --- a/golang/cosmos/x/vstorage/capdata/capdata_test.go +++ b/golang/cosmos/x/vstorage/capdata/capdata_test.go @@ -13,7 +13,7 @@ func ptr[T any](v T) *T { } func mustJsonMarshal(val any) string { - jsonText, err := json.Marshal(val) + jsonText, err := JsonMarshal(val) if err != nil { panic(err) } @@ -38,6 +38,35 @@ func remotableToString(r *CapdataRemotable) interface{} { return fmt.Sprintf("remotable:%s{%s}", iface, r.Id) } +func Test_JsonMarshal(t *testing.T) { + type testCase struct { + input string + expected string + errContains *string + } + testCases := []testCase{ + { + input: "<>&\u2028\u2029", + expected: `"<>&\u2028\u2029"`, + }, + } + for _, desc := range testCases { + label := fmt.Sprintf("%q", desc.input) + result, err := JsonMarshal(desc.input) + if desc.errContains == nil { + if err != nil { + t.Errorf("%s: got unexpected error %v", label, err) + } else if string(result) != desc.expected { + t.Errorf("%s: wrong result: %#q", label, result) + } + } else if err == nil { + t.Errorf("%s: got no error, want error %q", label, *desc.errContains) + } else if !strings.Contains(err.Error(), *desc.errContains) { + t.Errorf("%s: got error %v, want error %q", label, err, *desc.errContains) + } + } +} + func Test_DecodeSerializedCapdata(t *testing.T) { type testCase struct { format string @@ -307,7 +336,7 @@ func Test_DecodeSerializedCapdata(t *testing.T) { if err != nil { t.Errorf("%s: got unexpected error %v", label, err) } else if !reflect.DeepEqual(got, expected) { - result, err := json.Marshal(got) + result, err := JsonMarshal(got) if err != nil { panic(fmt.Errorf("%s: %v", label, err)) } diff --git a/golang/cosmos/x/vstorage/keeper/grpc_query.go b/golang/cosmos/x/vstorage/keeper/grpc_query.go index 15356314632..c8b00fa74af 100644 --- a/golang/cosmos/x/vstorage/keeper/grpc_query.go +++ b/golang/cosmos/x/vstorage/keeper/grpc_query.go @@ -97,10 +97,13 @@ var capDataRemotableValueFormats = map[string]string{ // cf. https://github.com/Agoric/agoric-sdk/blob/6e5b422b80e47c4dac151404f43faea5ab41e9b0/scripts/get-flattened-publication.sh func flatten(input interface{}, output map[string]interface{}, key string, top bool) error { // Act on the raw representation of a Remotable. - if capdata, ok := input.(*capdata.CapdataRemotable); ok { - repr, _ := json.Marshal(capdata) + if remotable, ok := input.(*capdata.CapdataRemotable); ok { var replacement interface{} - if err := json.Unmarshal(repr, &replacement); err != nil { + repr, err := capdata.JsonMarshal(remotable) + if err == nil { + err = json.Unmarshal(repr, &replacement) + } + if err != nil { return err } input = replacement @@ -225,13 +228,11 @@ func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*ty } switch mediaType { case JSONLines: - transformedJson, err := json.Marshal(item) + jsonText, err := capdata.JsonMarshal(item) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - responseItems[i] = string(transformedJson) - default: - return nil, status.Error(codes.InvalidArgument, "invalid media_type") + responseItems[i] = string(jsonText) } } diff --git a/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go b/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go index 84437c0fba9..38478aa90e9 100644 --- a/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go +++ b/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go @@ -1,7 +1,6 @@ package keeper import ( - "encoding/json" "fmt" "reflect" "strings" @@ -10,7 +9,7 @@ import ( grpcCodes "google.golang.org/grpc/codes" grpcStatus "google.golang.org/grpc/status" - //"github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/capdata" + "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/capdata" "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -21,7 +20,7 @@ func ptr[T any](v T) *T { } func mustJsonMarshal(val any) string { - jsonText, err := json.Marshal(val) + jsonText, err := capdata.JsonMarshal(val) if err != nil { panic(err) } From e2cbffaccbd1da7f38c2358fd4d1fbbc2e1abd9c Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Thu, 27 Jul 2023 12:54:50 -0400 Subject: [PATCH 11/11] feat(cosmos): Always include alleged name and slot id in vstorage CapData remotable representations --- golang/cosmos/x/vstorage/keeper/grpc_query.go | 25 ++++++++++--------- .../x/vstorage/keeper/keeper_grpc_test.go | 20 +++++++-------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/golang/cosmos/x/vstorage/keeper/grpc_query.go b/golang/cosmos/x/vstorage/keeper/grpc_query.go index c8b00fa74af..49bc47af39c 100644 --- a/golang/cosmos/x/vstorage/keeper/grpc_query.go +++ b/golang/cosmos/x/vstorage/keeper/grpc_query.go @@ -142,24 +142,24 @@ func capdataBigintToDigits(bigint *capdata.CapdataBigint) interface{} { } // capdataRemotableToString represents a Remotable as a bracketed string -// containing its alleged name (e.g., "[Foo {}]"). +// containing its alleged name and id from `slots` +// (e.g., "[Alleged: IST brand ]"). func capdataRemotableToString(r *capdata.CapdataRemotable) interface{} { iface := "Remotable" if r.Iface != nil || *r.Iface != "" { iface = *r.Iface } - return fmt.Sprintf("[%s {}]", iface) + return fmt.Sprintf("[%s <%s>]", iface, r.Id) } // capdataRemotableToObject represents a Remotable as an object containing -// its id from `slots` and alleged name minus any "Alleged:" prefix or "brand" -// suffix (e.g., `{ "id": "board007", "allegedName": "IST" }`). +// its id from `slots` and its alleged name minus any "Alleged:" prefix +// (e.g., `{ "id": "board007", "allegedName": "IST brand" }`). func capdataRemotableToObject(r *capdata.CapdataRemotable) interface{} { iface := "Remotable" if r.Iface != nil || *r.Iface != "" { iface = *r.Iface iface, _ = strings.CutPrefix(iface, "Alleged: ") - iface, _ = strings.CutSuffix(iface, " brand") } return map[string]interface{}{"id": r.Id, "allegedName": iface} } @@ -177,6 +177,9 @@ func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*ty Bigint: capdataBigintToDigits, } + // A response Value is "". + prefix, separator, suffix := "", "\n", "" + // Read options. mediaType, ok := capDataResponseMediaTypes[req.MediaType] if !ok { @@ -186,14 +189,12 @@ func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*ty if !ok { return nil, status.Error(codes.InvalidArgument, "invalid item_format") } - remotableFormat, ok := capDataRemotableValueFormats[req.RemotableValueFormat] - if !ok { + switch remotableFormat, ok := capDataRemotableValueFormats[req.RemotableValueFormat]; { + case !ok: return nil, status.Error(codes.InvalidArgument, "invalid remotable_value_format") - } - switch remotableFormat { - case FormatRemotableAsObject: + case remotableFormat == FormatRemotableAsObject: valueTransformations.Remotable = capdataRemotableToObject - case FormatRemotableAsString: + case remotableFormat == FormatRemotableAsString: valueTransformations.Remotable = capdataRemotableToString } @@ -238,7 +239,7 @@ func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*ty return &types.QueryCapDataResponse{ BlockHeight: cell.BlockHeight, - Value: strings.Join(responseItems, "\n"), + Value: prefix + strings.Join(responseItems, separator) + suffix, }, nil } diff --git a/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go b/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go index 38478aa90e9..b5883d61c3e 100644 --- a/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go +++ b/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go @@ -156,11 +156,11 @@ func TestCapData(t *testing.T) { // and deriving expectations from marshalling to avoid spurious mismatches // from Go's unpredictable field ordering (e.g., `{"a":0,"b":1}` vs. `{"b":1,"a":0}`). slots := []any{"a"} - deepSmallcapsBody := `{"arr":[{"bigint":"+42","remotable":"$0.Foo","ref2":"$0"}]}` + deepSmallcapsBody := `{"arr":[{"bigint":"+42","remotable":"$0.Alleged: Foo brand","ref2":"$0"}]}` deepLegacyBody := deepSmallcapsBody legacyFromSmallcaps := [][2]string{ [2]string{`"+42"`, `{"@qclass":"bigint","digits":"42"}`}, - [2]string{`"$0.Foo"`, `{"@qclass":"slot","index":0,"iface":"Foo"}`}, + [2]string{`"$0.Alleged: Foo brand"`, `{"@qclass":"slot","index":0,"iface":"Alleged: Foo brand"}`}, [2]string{`"$0"`, `{"@qclass":"slot","index":0}`}, } for _, pair := range legacyFromSmallcaps { @@ -183,8 +183,8 @@ func TestCapData(t *testing.T) { "arr": []any{ map[string]any{ "bigint": "42", - "remotable": "[Foo {}]", - "ref2": "[Foo {}]", + "remotable": "[Alleged: Foo brand ]", + "ref2": "[Alleged: Foo brand ]", }, }, }), @@ -197,8 +197,8 @@ func TestCapData(t *testing.T) { BlockHeight: "1", Value: mustMarshalTwoLines(map[string]any{ "arr-0-bigint": "42", - "arr-0-remotable": "[Foo {}]", - "arr-0-ref2": "[Foo {}]", + "arr-0-remotable": "[Alleged: Foo brand ]", + "arr-0-ref2": "[Alleged: Foo brand ]", }), }, }) @@ -211,8 +211,8 @@ func TestCapData(t *testing.T) { "arr": []any{ map[string]any{ "bigint": "42", - "remotable": map[string]any{"id": "a", "allegedName": "Foo"}, - "ref2": map[string]any{"id": "a", "allegedName": "Foo"}, + "remotable": map[string]any{"id": "a", "allegedName": "Foo brand"}, + "ref2": map[string]any{"id": "a", "allegedName": "Foo brand"}, }, }, }), @@ -226,9 +226,9 @@ func TestCapData(t *testing.T) { Value: mustMarshalTwoLines(map[string]any{ "arr-0-bigint": "42", "arr-0-remotable-id": "a", - "arr-0-remotable-allegedName": "Foo", + "arr-0-remotable-allegedName": "Foo brand", "arr-0-ref2-id": "a", - "arr-0-ref2-allegedName": "Foo", + "arr-0-ref2-allegedName": "Foo brand", }), }, })