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/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/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 diff --git a/golang/cosmos/x/vstorage/capdata/capdata.go b/golang/cosmos/x/vstorage/capdata/capdata.go new file mode 100644 index 00000000000..57d60fa7b39 --- /dev/null +++ b/golang/cosmos/x/vstorage/capdata/capdata.go @@ -0,0 +1,298 @@ +package capdata + +import ( + "bytes" + "encoding/json" + "fmt" + "regexp" + "strconv" + "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 { + 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 JsonMarshal(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 slot 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("unrecognized @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 +} 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..8a83b90a95f --- /dev/null +++ b/golang/cosmos/x/vstorage/capdata/capdata_test.go @@ -0,0 +1,352 @@ +package capdata + +import ( + "encoding/json" + "fmt" + "reflect" + "strings" + "testing" +) + +func ptr[T any](v T) *T { + return &v +} + +func mustJsonMarshal(val any) string { + jsonText, err := JsonMarshal(val) + if err != nil { + panic(err) + } + return string(jsonText) +} + +func mustJsonUnmarshal(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_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 + 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 != "" { + mustJsonUnmarshal(desc.expected, &expected) + } else { + mustJsonUnmarshal(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(mustJsonMarshal(capdata), desc.transformations) + // Replace each Remotable with its representation before comparing. + var got interface{} + mustJsonUnmarshal(mustJsonMarshal(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 := JsonMarshal(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) + } + } + } +} diff --git a/golang/cosmos/x/vstorage/keeper/grpc_query.go b/golang/cosmos/x/vstorage/keeper/grpc_query.go index 2f365c7a243..49bc47af39c 100644 --- a/golang/cosmos/x/vstorage/keeper/grpc_query.go +++ b/golang/cosmos/x/vstorage/keeper/grpc_query.go @@ -2,10 +2,14 @@ package keeper import ( "context" + "encoding/json" + "fmt" + "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,11 @@ type Querier struct { 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") @@ -30,6 +39,218 @@ 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 because both formats are lossy. +} + +// 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 remotable, ok := input.(*capdata.CapdataRemotable); ok { + var replacement interface{} + repr, err := capdata.JsonMarshal(remotable) + if err == nil { + err = json.Unmarshal(repr, &replacement) + } + if err != nil { + return err + } + input = replacement + } + + childKeyPrefix := key + if !top { + childKeyPrefix = childKeyPrefix + "-" + } + if arr, ok := input.([]interface{}); ok { + for i, v := range arr { + 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 { + if err := flatten(v, output, childKeyPrefix+k, false); err != nil { + return err + } + } + } 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 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 <%s>]", iface, r.Id) +} + +// capdataRemotableToObject represents a Remotable as an object containing +// 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: ") + } + 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") + } + ctx := sdk.UnwrapSDKContext(c) + + valueTransformations := capdata.CapdataValueTransformations{ + Bigint: capdataBigintToDigits, + } + + // A response Value is "". + prefix, separator, suffix := "", "\n", "" + + // 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") + } + switch remotableFormat, ok := capDataRemotableValueFormats[req.RemotableValueFormat]; { + case !ok: + return nil, status.Error(codes.InvalidArgument, "invalid remotable_value_format") + case remotableFormat == FormatRemotableAsObject: + valueTransformations.Remotable = capdataRemotableToObject + case remotableFormat == 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: + jsonText, err := capdata.JsonMarshal(item) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + responseItems[i] = string(jsonText) + } + } + + return &types.QueryCapDataResponse{ + BlockHeight: cell.BlockHeight, + Value: prefix + strings.Join(responseItems, separator) + suffix, + }, nil +} + +// =================================================================== +// /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") 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..b5883d61c3e --- /dev/null +++ b/golang/cosmos/x/vstorage/keeper/keeper_grpc_test.go @@ -0,0 +1,299 @@ +package keeper + +import ( + "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 := capdata.JsonMarshal(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.Alleged: Foo brand","ref2":"$0"}]}` + deepLegacyBody := deepSmallcapsBody + legacyFromSmallcaps := [][2]string{ + [2]string{`"+42"`, `{"@qclass":"bigint","digits":"42"}`}, + [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 { + 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": "[Alleged: Foo brand ]", + "ref2": "[Alleged: Foo brand ]", + }, + }, + }), + }, + }) + 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": "[Alleged: Foo brand ]", + "arr-0-ref2": "[Alleged: Foo brand ]", + }), + }, + }) + 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 brand"}, + "ref2": map[string]any{"id": "a", "allegedName": "Foo brand"}, + }, + }, + }), + }, + }) + 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 brand", + "arr-0-ref2-id": "a", + "arr-0-ref2-allegedName": "Foo brand", + }), + }, + }) + + // 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) + } + } +} 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 )