@agoric/cosmos 0.35.0-u12.0 → 0.35.0-u14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/Makefile +25 -12
  3. package/ante/ante.go +7 -5
  4. package/ante/inbound_test.go +8 -0
  5. package/app/app.go +139 -108
  6. package/app/export.go +13 -9
  7. package/app/sim_test.go +4 -4
  8. package/cmd/agd/main.go +5 -3
  9. package/cmd/libdaemon/main.go +5 -2
  10. package/daemon/cmd/genaccounts.go +13 -9
  11. package/daemon/cmd/root.go +38 -17
  12. package/daemon/cmd/root_test.go +1 -1
  13. package/daemon/cmd/testnet.go +17 -6
  14. package/daemon/main.go +3 -2
  15. package/git-revision.txt +1 -1
  16. package/go.mod +117 -76
  17. package/go.sum +858 -210
  18. package/package.json +3 -3
  19. package/proto/agoric/vstorage/query.proto +53 -1
  20. package/scripts/protocgen.sh +12 -1
  21. package/third_party/proto/buf.yaml +1 -0
  22. package/third_party/proto/cosmos/base/query/v1beta1/pagination.proto +4 -1
  23. package/third_party/proto/cosmos/base/v1beta1/coin.proto +7 -4
  24. package/third_party/proto/cosmos/upgrade/v1beta1/upgrade.proto +16 -6
  25. package/third_party/proto/cosmos_proto/cosmos.proto +97 -0
  26. package/third_party/proto/google/api/annotations.proto +1 -1
  27. package/third_party/proto/google/api/http.proto +181 -120
  28. package/third_party/proto/google/api/httpbody.proto +9 -6
  29. package/third_party/proto/google/protobuf/any.proto +1 -7
  30. package/third_party/proto/ibc/core/channel/v1/channel.proto +15 -1
  31. package/third_party/proto/ibc/core/client/v1/client.proto +9 -6
  32. package/upgradegaia.sh +13 -4
  33. package/vm/action.go +133 -0
  34. package/vm/action_test.go +129 -0
  35. package/vm/controller.go +9 -16
  36. package/vm/core_proposals.go +31 -0
  37. package/x/lien/keeper/account.go +16 -11
  38. package/x/lien/keeper/keeper.go +5 -4
  39. package/x/lien/keeper/keeper_test.go +9 -9
  40. package/x/lien/lien.go +6 -4
  41. package/x/lien/lien_test.go +20 -16
  42. package/x/swingset/abci.go +25 -33
  43. package/x/swingset/client/cli/query.go +2 -2
  44. package/x/swingset/client/cli/tx.go +48 -33
  45. package/x/swingset/client/proposal_handler.go +2 -17
  46. package/x/swingset/keeper/keeper.go +69 -15
  47. package/x/swingset/keeper/keeper_test.go +1 -1
  48. package/x/swingset/keeper/migrations.go +7 -2
  49. package/x/swingset/keeper/msg_server.go +66 -49
  50. package/x/swingset/keeper/proposal.go +14 -8
  51. package/x/swingset/keeper/querier.go +14 -6
  52. package/x/swingset/keeper/swing_store_exports_handler.go +5 -1
  53. package/x/swingset/proposal_handler.go +3 -3
  54. package/x/swingset/swingset.go +4 -2
  55. package/x/swingset/types/codec.go +2 -2
  56. package/x/swingset/types/default-params.go +22 -16
  57. package/x/swingset/types/expected_keepers.go +11 -0
  58. package/x/swingset/types/msgs.go +43 -2
  59. package/x/swingset/types/msgs.pb.go +16 -16
  60. package/x/swingset/types/params.go +74 -0
  61. package/x/swingset/types/params_test.go +116 -0
  62. package/x/swingset/types/proposal.go +5 -5
  63. package/x/swingset/types/types.go +30 -28
  64. package/x/vbank/keeper/keeper.go +3 -2
  65. package/x/vbank/keeper/querier.go +6 -2
  66. package/x/vbank/keeper/rewards.go +1 -1
  67. package/x/vbank/vbank.go +19 -17
  68. package/x/vbank/vbank_test.go +18 -18
  69. package/x/vibc/handler.go +3 -8
  70. package/x/vibc/ibc.go +79 -126
  71. package/x/vibc/keeper/keeper.go +19 -18
  72. package/x/vibc/types/expected_keepers.go +13 -5
  73. package/x/vibc/types/msgs.go +1 -1
  74. package/x/vibc/types/msgs.pb.go +1 -1
  75. package/x/vstorage/README.md +138 -0
  76. package/x/vstorage/capdata/capdata.go +298 -0
  77. package/x/vstorage/capdata/capdata_test.go +352 -0
  78. package/x/vstorage/client/cli/query.go +51 -4
  79. package/x/vstorage/keeper/grpc_query.go +221 -0
  80. package/x/vstorage/keeper/keeper.go +3 -2
  81. package/x/vstorage/keeper/keeper_grpc_test.go +300 -0
  82. package/x/vstorage/keeper/keeper_test.go +1 -1
  83. package/x/vstorage/keeper/querier.go +6 -2
  84. package/x/vstorage/keeper/querier_test.go +112 -0
  85. package/x/vstorage/types/query.pb.go +646 -36
  86. package/x/vstorage/types/query.pb.gw.go +119 -0
  87. package/x/vstorage/vstorage.go +16 -15
  88. package/x/vstorage/vstorage_test.go +5 -5
  89. package/x/swingset/legacy/v32/params.go +0 -37
  90. package/x/swingset/legacy/v32/params_test.go +0 -133
  91. /package/{src/index.cjs → index.cjs} +0 -0
@@ -4,6 +4,7 @@ import (
4
4
  "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/types"
5
5
  "github.com/cosmos/cosmos-sdk/client"
6
6
  "github.com/cosmos/cosmos-sdk/client/flags"
7
+ "github.com/gogo/protobuf/proto"
7
8
  "github.com/spf13/cobra"
8
9
  )
9
10
 
@@ -18,6 +19,7 @@ func GetQueryCmd(storeKey string) *cobra.Command {
18
19
  swingsetQueryCmd.AddCommand(
19
20
  GetCmdGetData(storeKey),
20
21
  GetCmdGetChildren(storeKey),
22
+ GetCmdGetPath(storeKey),
21
23
  )
22
24
 
23
25
  return swingsetQueryCmd
@@ -26,8 +28,8 @@ func GetQueryCmd(storeKey string) *cobra.Command {
26
28
  // GetCmdGetData queries information about storage
27
29
  func GetCmdGetData(queryRoute string) *cobra.Command {
28
30
  cmd := &cobra.Command{
29
- Use: "data [path]",
30
- Short: "get vstorage data for path",
31
+ Use: "data <path>",
32
+ Short: "get data for vstorage path",
31
33
  Args: cobra.ExactArgs(1),
32
34
  RunE: func(cmd *cobra.Command, args []string) error {
33
35
  clientCtx, err := client.GetClientQueryContext(cmd)
@@ -58,8 +60,12 @@ func GetCmdGetChildren(queryRoute string) *cobra.Command {
58
60
  cmd := &cobra.Command{
59
61
  Use: "children [path]",
60
62
  Aliases: []string{"keys"},
61
- Short: "get vstorage subkey names for path",
62
- Args: cobra.MaximumNArgs(1),
63
+ Short: "get child path segments under vstorage path",
64
+ Long: `get child path segments under vstorage path.
65
+ When absent, path defaults to the empty root path.
66
+ Path segments are dot-separated, so a child "baz" under path "foo.bar" has path
67
+ "foo.bar.baz".`,
68
+ Args: cobra.MaximumNArgs(1),
63
69
  RunE: func(cmd *cobra.Command, args []string) error {
64
70
  clientCtx, err := client.GetClientQueryContext(cmd)
65
71
  if err != nil {
@@ -86,3 +92,44 @@ func GetCmdGetChildren(queryRoute string) *cobra.Command {
86
92
  flags.AddQueryFlagsToCmd(cmd)
87
93
  return cmd
88
94
  }
95
+
96
+ // GetCmdGetPath queries vstorage data or children, depending on the path
97
+ func GetCmdGetPath(queryRoute string) *cobra.Command {
98
+ cmd := &cobra.Command{
99
+ Use: "path <path>",
100
+ Short: "get vstorage data, or children if path ends with '.'",
101
+ Args: cobra.ExactArgs(1),
102
+ RunE: func(cmd *cobra.Command, args []string) error {
103
+ clientCtx, err := client.GetClientQueryContext(cmd)
104
+ if err != nil {
105
+ return err
106
+ }
107
+ queryClient := types.NewQueryClient(clientCtx)
108
+
109
+ path := args[0]
110
+
111
+ var res proto.Message
112
+
113
+ // if path ends with '.' then remove it and query children
114
+ if path[len(path)-1] == '.' {
115
+ path = path[:len(path)-1]
116
+ res, err = queryClient.Children(cmd.Context(), &types.QueryChildrenRequest{
117
+ Path: path,
118
+ })
119
+ } else {
120
+ res, err = queryClient.Data(cmd.Context(), &types.QueryDataRequest{
121
+ Path: path,
122
+ })
123
+ }
124
+
125
+ if err != nil {
126
+ return err
127
+ }
128
+
129
+ return clientCtx.PrintProto(res)
130
+ },
131
+ }
132
+
133
+ flags.AddQueryFlagsToCmd(cmd)
134
+ return cmd
135
+ }
@@ -2,10 +2,14 @@ package keeper
2
2
 
3
3
  import (
4
4
  "context"
5
+ "encoding/json"
6
+ "fmt"
7
+ "strings"
5
8
 
6
9
  "google.golang.org/grpc/codes"
7
10
  "google.golang.org/grpc/status"
8
11
 
12
+ "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/capdata"
9
13
  "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/types"
10
14
  sdk "github.com/cosmos/cosmos-sdk/types"
11
15
  )
@@ -17,6 +21,11 @@ type Querier struct {
17
21
 
18
22
  var _ types.QueryServer = Querier{}
19
23
 
24
+ // ===================================================================
25
+ // /agoric.vstorage.Query/Data
26
+ // ===================================================================
27
+
28
+ // /agoric.vstorage.Query/Data returns data for a specified path.
20
29
  func (k Querier) Data(c context.Context, req *types.QueryDataRequest) (*types.QueryDataResponse, error) {
21
30
  if req == nil {
22
31
  return nil, status.Error(codes.InvalidArgument, "empty request")
@@ -30,6 +39,218 @@ func (k Querier) Data(c context.Context, req *types.QueryDataRequest) (*types.Qu
30
39
  }, nil
31
40
  }
32
41
 
42
+ // ===================================================================
43
+ // /agoric.vstorage.Query/CapData
44
+ // ===================================================================
45
+
46
+ const (
47
+ // Media types.
48
+ JSONLines = "JSON Lines"
49
+
50
+ // CapData transformation formats.
51
+ FormatCapDataFlat = "flat"
52
+
53
+ // CapData remotable value formats.
54
+ FormatRemotableAsObject = "object"
55
+ FormatRemotableAsString = "string"
56
+ )
57
+
58
+ var capDataResponseMediaTypes = map[string]string{
59
+ JSONLines: JSONLines,
60
+ // Default to JSON Lines.
61
+ "": JSONLines,
62
+ }
63
+ var capDataTransformationFormats = map[string]string{
64
+ FormatCapDataFlat: FormatCapDataFlat,
65
+ // Default to no transformation.
66
+ "": "",
67
+ }
68
+ var capDataRemotableValueFormats = map[string]string{
69
+ FormatRemotableAsObject: FormatRemotableAsObject,
70
+ FormatRemotableAsString: FormatRemotableAsString,
71
+ // No default because both formats are lossy.
72
+ }
73
+
74
+ // flatten converts data into a flat structure in which each deep leaf entry is replaced with
75
+ // a top-level entry having the same value but a key generated by joining the keys on its path
76
+ // with separating dashes.
77
+ // For example,
78
+ // ```
79
+ // { "contacts": [
80
+ //
81
+ // { "name": "Alice", "email": "a@example.com" },
82
+ // { "name": "Bob", "email": "b@example.com" }
83
+ //
84
+ // ] }
85
+ // ```
86
+ // becomes
87
+ // ```
88
+ //
89
+ // {
90
+ // "contacts-0-name": "Alice",
91
+ // "contacts-0-email": "a@example.com",
92
+ // "contacts-1-name": "Bob",
93
+ // "contacts-1-email": "b@example.com"
94
+ // }
95
+ //
96
+ // ```
97
+ // cf. https://github.com/Agoric/agoric-sdk/blob/6e5b422b80e47c4dac151404f43faea5ab41e9b0/scripts/get-flattened-publication.sh
98
+ func flatten(input interface{}, output map[string]interface{}, key string, top bool) error {
99
+ // Act on the raw representation of a Remotable.
100
+ if remotable, ok := input.(*capdata.CapdataRemotable); ok {
101
+ var replacement interface{}
102
+ repr, err := capdata.JsonMarshal(remotable)
103
+ if err == nil {
104
+ err = json.Unmarshal(repr, &replacement)
105
+ }
106
+ if err != nil {
107
+ return err
108
+ }
109
+ input = replacement
110
+ }
111
+
112
+ childKeyPrefix := key
113
+ if !top {
114
+ childKeyPrefix = childKeyPrefix + "-"
115
+ }
116
+ if arr, ok := input.([]interface{}); ok {
117
+ for i, v := range arr {
118
+ if err := flatten(v, output, childKeyPrefix+fmt.Sprintf("%d", i), false); err != nil {
119
+ return err
120
+ }
121
+ }
122
+ } else if obj, ok := input.(map[string]interface{}); ok {
123
+ for k, v := range obj {
124
+ if err := flatten(v, output, childKeyPrefix+k, false); err != nil {
125
+ return err
126
+ }
127
+ }
128
+ } else {
129
+ if _, has := output[key]; has {
130
+ return fmt.Errorf("key conflict: %q", key)
131
+ }
132
+ output[key] = input
133
+ }
134
+ return nil
135
+ }
136
+
137
+ // capdataBigintToDigits represents a bigint as a string consisting of
138
+ // an optional "-" followed by a sequence of digits with no extraneous zeroes
139
+ // (e.g., "0" or "-40").
140
+ func capdataBigintToDigits(bigint *capdata.CapdataBigint) interface{} {
141
+ return bigint.Normalized
142
+ }
143
+
144
+ // capdataRemotableToString represents a Remotable as a bracketed string
145
+ // containing its alleged name and id from `slots`
146
+ // (e.g., "[Alleged: IST brand <board007>]").
147
+ func capdataRemotableToString(r *capdata.CapdataRemotable) interface{} {
148
+ iface := "Remotable"
149
+ if r.Iface != nil || *r.Iface != "" {
150
+ iface = *r.Iface
151
+ }
152
+ return fmt.Sprintf("[%s <%s>]", iface, r.Id)
153
+ }
154
+
155
+ // capdataRemotableToObject represents a Remotable as an object containing
156
+ // its id from `slots` and its alleged name minus any "Alleged:" prefix
157
+ // (e.g., `{ "id": "board007", "allegedName": "IST brand" }`).
158
+ func capdataRemotableToObject(r *capdata.CapdataRemotable) interface{} {
159
+ iface := "Remotable"
160
+ if r.Iface != nil || *r.Iface != "" {
161
+ iface = *r.Iface
162
+ iface, _ = strings.CutPrefix(iface, "Alleged: ")
163
+ }
164
+ return map[string]interface{}{"id": r.Id, "allegedName": iface}
165
+ }
166
+
167
+ // /agoric.vstorage.Query/CapData returns data for a specified path,
168
+ // interpreted as CapData in a StreamCell (auto-promoting isolated CapData
169
+ // into a single-item StreamCell) and transformed as specified.
170
+ func (k Querier) CapData(c context.Context, req *types.QueryCapDataRequest) (*types.QueryCapDataResponse, error) {
171
+ if req == nil {
172
+ return nil, status.Error(codes.InvalidArgument, "empty request")
173
+ }
174
+ ctx := sdk.UnwrapSDKContext(c)
175
+
176
+ valueTransformations := capdata.CapdataValueTransformations{
177
+ Bigint: capdataBigintToDigits,
178
+ }
179
+
180
+ // A response Value is "<prefix><separator-joined items><suffix>".
181
+ prefix, separator, suffix := "", "\n", ""
182
+
183
+ // Read options.
184
+ mediaType, ok := capDataResponseMediaTypes[req.MediaType]
185
+ if !ok {
186
+ return nil, status.Error(codes.InvalidArgument, "invalid media_type")
187
+ }
188
+ transformation, ok := capDataTransformationFormats[req.ItemFormat]
189
+ if !ok {
190
+ return nil, status.Error(codes.InvalidArgument, "invalid item_format")
191
+ }
192
+ switch remotableFormat, ok := capDataRemotableValueFormats[req.RemotableValueFormat]; {
193
+ case !ok:
194
+ return nil, status.Error(codes.InvalidArgument, "invalid remotable_value_format")
195
+ case remotableFormat == FormatRemotableAsObject:
196
+ valueTransformations.Remotable = capdataRemotableToObject
197
+ case remotableFormat == FormatRemotableAsString:
198
+ valueTransformations.Remotable = capdataRemotableToString
199
+ }
200
+
201
+ // Read data, auto-upgrading a standalone value to a single-value StreamCell.
202
+ entry := k.GetEntry(ctx, req.Path)
203
+ if !entry.HasValue() {
204
+ return nil, status.Error(codes.FailedPrecondition, "no data")
205
+ }
206
+ value := entry.StringValue()
207
+ var cell StreamCell
208
+ _ = json.Unmarshal([]byte(value), &cell)
209
+ if cell.BlockHeight == "" {
210
+ cell = StreamCell{Values: []string{value}}
211
+ }
212
+
213
+ // Format each StreamCell value.
214
+ responseItems := make([]string, len(cell.Values))
215
+ for i, capDataJson := range cell.Values {
216
+ item, err := capdata.DecodeSerializedCapdata(capDataJson, valueTransformations)
217
+ if err != nil {
218
+ return nil, status.Error(codes.FailedPrecondition, err.Error())
219
+ }
220
+ if transformation == FormatCapDataFlat {
221
+ flattened := map[string]interface{}{}
222
+ if err := flatten(item, flattened, "", true); err != nil {
223
+ return nil, status.Error(codes.Internal, err.Error())
224
+ }
225
+ // Replace the item, unless it was a scalar that "flattened" to `{ "": ... }`.
226
+ if _, singleton := flattened[""]; !singleton {
227
+ item = flattened
228
+ }
229
+ }
230
+ switch mediaType {
231
+ case JSONLines:
232
+ jsonText, err := capdata.JsonMarshal(item)
233
+ if err != nil {
234
+ return nil, status.Error(codes.Internal, err.Error())
235
+ }
236
+ responseItems[i] = string(jsonText)
237
+ }
238
+ }
239
+
240
+ return &types.QueryCapDataResponse{
241
+ BlockHeight: cell.BlockHeight,
242
+ Value: prefix + strings.Join(responseItems, separator) + suffix,
243
+ }, nil
244
+ }
245
+
246
+ // ===================================================================
247
+ // /agoric.vstorage.Query/Children
248
+ // ===================================================================
249
+
250
+ // /agoric.vstorage.Query/Children returns the list of path segments
251
+ // that exist immediately underneath a specified path, including
252
+ // those corresponding with "empty non-terminals" having children
253
+ // but no data of their own.
33
254
  func (k Querier) Children(c context.Context, req *types.QueryChildrenRequest) (*types.QueryChildrenResponse, error) {
34
255
  if req == nil {
35
256
  return nil, status.Error(codes.InvalidArgument, "empty request")
@@ -10,6 +10,7 @@ import (
10
10
  "strconv"
11
11
  "strings"
12
12
 
13
+ storetypes "github.com/cosmos/cosmos-sdk/store/types"
13
14
  sdk "github.com/cosmos/cosmos-sdk/types"
14
15
  db "github.com/tendermint/tm-db"
15
16
 
@@ -62,7 +63,7 @@ func cutPrefix(s, prefix []byte) (after []byte, found bool) {
62
63
  // for the various parts of the state machine
63
64
  type Keeper struct {
64
65
  changeManager ChangeManager
65
- storeKey sdk.StoreKey
66
+ storeKey storetypes.StoreKey
66
67
  }
67
68
 
68
69
  func (bcm *BatchingChangeManager) Track(ctx sdk.Context, k Keeper, entry agoric.KVEntry, isLegacy bool) {
@@ -116,7 +117,7 @@ func NewBatchingChangeManager() *BatchingChangeManager {
116
117
  return &bcm
117
118
  }
118
119
 
119
- func NewKeeper(storeKey sdk.StoreKey) Keeper {
120
+ func NewKeeper(storeKey storetypes.StoreKey) Keeper {
120
121
  return Keeper{
121
122
  storeKey: storeKey,
122
123
  changeManager: NewBatchingChangeManager(),
@@ -0,0 +1,300 @@
1
+ package keeper
2
+
3
+ import (
4
+ "fmt"
5
+ "reflect"
6
+ "strings"
7
+ "testing"
8
+
9
+ grpcCodes "google.golang.org/grpc/codes"
10
+ grpcStatus "google.golang.org/grpc/status"
11
+
12
+ agoric "github.com/Agoric/agoric-sdk/golang/cosmos/types"
13
+ "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/capdata"
14
+ "github.com/Agoric/agoric-sdk/golang/cosmos/x/vstorage/types"
15
+
16
+ sdk "github.com/cosmos/cosmos-sdk/types"
17
+ )
18
+
19
+ func ptr[T any](v T) *T {
20
+ return &v
21
+ }
22
+
23
+ func mustJsonMarshal(val any) string {
24
+ jsonText, err := capdata.JsonMarshal(val)
25
+ if err != nil {
26
+ panic(err)
27
+ }
28
+ return string(jsonText)
29
+ }
30
+
31
+ func mustMarshalStreamCell(blockHeight string, values []string) string {
32
+ cell := map[string]any{
33
+ "blockHeight": blockHeight,
34
+ "values": values,
35
+ }
36
+ return mustJsonMarshal(cell)
37
+ }
38
+
39
+ func TestCapData(t *testing.T) {
40
+ testKit := makeTestKit()
41
+ ctx, keeper := testKit.ctx, testKit.vstorageKeeper
42
+ querier := Querier{keeper}
43
+
44
+ type testCase struct {
45
+ label string
46
+ data *string
47
+ request types.QueryCapDataRequest
48
+ expected types.QueryCapDataResponse
49
+ errCode grpcCodes.Code
50
+ errContains *string
51
+ }
52
+ testCases := []testCase{}
53
+
54
+ // Test simple cases (various kinds of bad data up to simple flat JSON-compatible CapData).
55
+ decodableSmallcaps := `{"body":"#true","slots":[]}`
56
+ decodableLegacy := `{"body":"true","slots":[]}`
57
+ testCases = append(testCases, []testCase{
58
+ {label: "no data",
59
+ data: nil,
60
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
61
+ errCode: grpcCodes.FailedPrecondition,
62
+ errContains: ptr("no data"),
63
+ },
64
+ {label: "zero-length data",
65
+ data: ptr(""),
66
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
67
+ errCode: grpcCodes.FailedPrecondition,
68
+ errContains: ptr("JSON"),
69
+ },
70
+ {label: "cell with empty string",
71
+ data: ptr(mustMarshalStreamCell("1", []string{decodableSmallcaps, "", decodableLegacy})),
72
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
73
+ errCode: grpcCodes.FailedPrecondition,
74
+ errContains: ptr("JSON"),
75
+ },
76
+ {label: "non-JSON data",
77
+ data: ptr("foo"),
78
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
79
+ errCode: grpcCodes.FailedPrecondition,
80
+ errContains: ptr("invalid"),
81
+ },
82
+ {label: "cell with non-JSON",
83
+ data: ptr(mustMarshalStreamCell("1", []string{decodableSmallcaps, "foo", decodableLegacy})),
84
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
85
+ errCode: grpcCodes.FailedPrecondition,
86
+ errContains: ptr("invalid"),
87
+ },
88
+ {label: "lone non-CapData",
89
+ data: ptr("{}"),
90
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
91
+ errCode: grpcCodes.FailedPrecondition,
92
+ errContains: ptr("invalid CapData"),
93
+ },
94
+ {label: "cell with non-CapData",
95
+ data: ptr(mustMarshalStreamCell("1", []string{decodableSmallcaps, "{}", decodableLegacy})),
96
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
97
+ errCode: grpcCodes.FailedPrecondition,
98
+ errContains: ptr("invalid CapData"),
99
+ },
100
+ {label: "lone smallcaps CapData",
101
+ data: ptr(decodableSmallcaps),
102
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
103
+ expected: types.QueryCapDataResponse{Value: `true`},
104
+ },
105
+ {label: "cell with smallcaps CapData",
106
+ data: ptr(mustMarshalStreamCell("1", []string{decodableSmallcaps})),
107
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
108
+ expected: types.QueryCapDataResponse{BlockHeight: "1", Value: `true`},
109
+ },
110
+ {label: "lone legacy CapData",
111
+ data: ptr(decodableLegacy),
112
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
113
+ expected: types.QueryCapDataResponse{Value: `true`},
114
+ },
115
+ {label: "cell with legacy CapData",
116
+ data: ptr(mustMarshalStreamCell("1", []string{decodableLegacy})),
117
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
118
+ expected: types.QueryCapDataResponse{BlockHeight: "1", Value: `true`},
119
+ },
120
+ }...)
121
+
122
+ // Test option validation.
123
+ testCases = append(testCases, []testCase{
124
+ {label: "explicit JSON Lines",
125
+ data: ptr(decodableSmallcaps),
126
+ request: types.QueryCapDataRequest{MediaType: "JSON Lines", RemotableValueFormat: "string"},
127
+ expected: types.QueryCapDataResponse{Value: `true`},
128
+ },
129
+ {label: "invalid media type",
130
+ data: ptr(decodableSmallcaps),
131
+ request: types.QueryCapDataRequest{MediaType: "JSONLines", RemotableValueFormat: "string"},
132
+ errCode: grpcCodes.InvalidArgument,
133
+ errContains: ptr("media_type"),
134
+ },
135
+ {label: "invalid item format",
136
+ data: ptr(decodableSmallcaps),
137
+ request: types.QueryCapDataRequest{ItemFormat: "deep", RemotableValueFormat: "string"},
138
+ errCode: grpcCodes.InvalidArgument,
139
+ errContains: ptr("item_format"),
140
+ },
141
+ {label: "missing remotable value format",
142
+ data: ptr(decodableSmallcaps),
143
+ request: types.QueryCapDataRequest{},
144
+ errCode: grpcCodes.InvalidArgument,
145
+ errContains: ptr("remotable_value_format"),
146
+ },
147
+ {label: "invalid remotable value format",
148
+ data: ptr(decodableSmallcaps),
149
+ request: types.QueryCapDataRequest{RemotableValueFormat: "foo"},
150
+ errCode: grpcCodes.InvalidArgument,
151
+ errContains: ptr("remotable_value_format"),
152
+ },
153
+ }...)
154
+
155
+ // Test formatting options against sufficiently complex CapData,
156
+ // deriving legacy encoding from smallcaps encoding to ensure equivalence
157
+ // and deriving expectations from marshalling to avoid spurious mismatches
158
+ // from Go's unpredictable field ordering (e.g., `{"a":0,"b":1}` vs. `{"b":1,"a":0}`).
159
+ slots := []any{"a"}
160
+ deepSmallcapsBody := `{"arr":[{"bigint":"+42","remotable":"$0.Alleged: Foo brand","ref2":"$0"}]}`
161
+ deepLegacyBody := deepSmallcapsBody
162
+ legacyFromSmallcaps := [][2]string{
163
+ [2]string{`"+42"`, `{"@qclass":"bigint","digits":"42"}`},
164
+ [2]string{`"$0.Alleged: Foo brand"`, `{"@qclass":"slot","index":0,"iface":"Alleged: Foo brand"}`},
165
+ [2]string{`"$0"`, `{"@qclass":"slot","index":0}`},
166
+ }
167
+ for _, pair := range legacyFromSmallcaps {
168
+ deepLegacyBody = strings.Replace(deepLegacyBody, pair[0], pair[1], -1)
169
+ }
170
+ cell := mustMarshalStreamCell("1", []string{
171
+ mustJsonMarshal(map[string]any{"body": "#" + deepSmallcapsBody, "slots": slots}),
172
+ mustJsonMarshal(map[string]any{"body": deepLegacyBody, "slots": slots}),
173
+ })
174
+ mustMarshalTwoLines := func(val any) string {
175
+ line := mustJsonMarshal(val)
176
+ return fmt.Sprintf("%s\n%s", line, line)
177
+ }
178
+ testCases = append(testCases, testCase{label: "remotables as strings",
179
+ data: ptr(cell),
180
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
181
+ expected: types.QueryCapDataResponse{
182
+ BlockHeight: "1",
183
+ Value: mustMarshalTwoLines(map[string]any{
184
+ "arr": []any{
185
+ map[string]any{
186
+ "bigint": "42",
187
+ "remotable": "[Alleged: Foo brand <a>]",
188
+ "ref2": "[Alleged: Foo brand <a>]",
189
+ },
190
+ },
191
+ }),
192
+ },
193
+ })
194
+ testCases = append(testCases, testCase{label: "remotables as strings, flat",
195
+ data: ptr(cell),
196
+ request: types.QueryCapDataRequest{ItemFormat: "flat", RemotableValueFormat: "string"},
197
+ expected: types.QueryCapDataResponse{
198
+ BlockHeight: "1",
199
+ Value: mustMarshalTwoLines(map[string]any{
200
+ "arr-0-bigint": "42",
201
+ "arr-0-remotable": "[Alleged: Foo brand <a>]",
202
+ "arr-0-ref2": "[Alleged: Foo brand <a>]",
203
+ }),
204
+ },
205
+ })
206
+ testCases = append(testCases, testCase{label: "remotables as objects",
207
+ data: ptr(cell),
208
+ request: types.QueryCapDataRequest{RemotableValueFormat: "object"},
209
+ expected: types.QueryCapDataResponse{
210
+ BlockHeight: "1",
211
+ Value: mustMarshalTwoLines(map[string]any{
212
+ "arr": []any{
213
+ map[string]any{
214
+ "bigint": "42",
215
+ "remotable": map[string]any{"id": "a", "allegedName": "Foo brand"},
216
+ "ref2": map[string]any{"id": "a", "allegedName": "Foo brand"},
217
+ },
218
+ },
219
+ }),
220
+ },
221
+ })
222
+ testCases = append(testCases, testCase{label: "remotables as objects, flat",
223
+ data: ptr(cell),
224
+ request: types.QueryCapDataRequest{ItemFormat: "flat", RemotableValueFormat: "object"},
225
+ expected: types.QueryCapDataResponse{
226
+ BlockHeight: "1",
227
+ Value: mustMarshalTwoLines(map[string]any{
228
+ "arr-0-bigint": "42",
229
+ "arr-0-remotable-id": "a",
230
+ "arr-0-remotable-allegedName": "Foo brand",
231
+ "arr-0-ref2-id": "a",
232
+ "arr-0-ref2-allegedName": "Foo brand",
233
+ }),
234
+ },
235
+ })
236
+
237
+ // Test errors from CapData that includes unsupported values.
238
+ expectNotImplemented := func(label, capdataBody string, slots []any) testCase {
239
+ if slots == nil {
240
+ slots = []any{}
241
+ }
242
+ serialized := mustJsonMarshal(map[string]any{
243
+ "body": capdataBody,
244
+ "slots": slots,
245
+ })
246
+ return testCase{
247
+ label: label,
248
+ data: ptr(serialized),
249
+ request: types.QueryCapDataRequest{RemotableValueFormat: "string"},
250
+ errCode: grpcCodes.FailedPrecondition,
251
+ errContains: ptr("not implemented"),
252
+ }
253
+ }
254
+ testCases = append(testCases, []testCase{
255
+ expectNotImplemented("smallcaps undefined", `#"#undefined"`, nil),
256
+ expectNotImplemented("smallcaps NaN", `#"#NaN"`, nil),
257
+ expectNotImplemented("smallcaps infinity", `#"#Infinity"`, nil),
258
+ expectNotImplemented("smallcaps negative infinity", `#"#-Infinity"`, nil),
259
+ expectNotImplemented("smallcaps symbol", `#"%foo"`, nil),
260
+ expectNotImplemented("smallcaps promise", `#"&0"`, []any{"a"}),
261
+ expectNotImplemented("smallcaps tagged", `#{"#tag":"copySet","payload":[]}`, nil),
262
+ expectNotImplemented("smallcaps error", `#{"#error":"foo","name":"Error"}`, nil),
263
+ expectNotImplemented("legacy undefined", `{"@qclass":"undefined"}`, nil),
264
+ expectNotImplemented("legacy NaN", `{"@qclass":"NaN"}`, nil),
265
+ expectNotImplemented("legacy infinity", `{"@qclass":"Infinity"}`, nil),
266
+ expectNotImplemented("legacy negative infinity", `{"@qclass":"-Infinity"}`, nil),
267
+ expectNotImplemented("legacy symbol", `{"@qclass":"symbol","name":"foo"}`, nil),
268
+ expectNotImplemented("smallcaps tagged", `{"@qclass":"tagged","tag":"copySet","payload":[]}`, nil),
269
+ expectNotImplemented("smallcaps error", `{"@qclass":"error","message":"foo","name":"Error"}`, nil),
270
+ expectNotImplemented("smallcaps Hilbert Hotel", `{"@qclass":"hilbert","original":"foo"}`, nil),
271
+ }...)
272
+ for _, desc := range testCases {
273
+ desc.request.Path = "key"
274
+ if desc.data == nil {
275
+ keeper.SetStorage(ctx, agoric.NewKVEntryWithNoValue(desc.request.Path))
276
+ } else {
277
+ keeper.SetStorage(ctx, agoric.NewKVEntry(desc.request.Path, *desc.data))
278
+ }
279
+ resp, err := querier.CapData(sdk.WrapSDKContext(ctx), &desc.request)
280
+ if desc.errCode == grpcCodes.OK {
281
+ if err != nil {
282
+ t.Errorf("%s: got unexpected error %v", desc.label, err)
283
+ } else if reflect.DeepEqual(resp, &desc.expected) {
284
+ continue
285
+ }
286
+ if resp.Value != desc.expected.Value {
287
+ lines := strings.Split(resp.Value, "\n")
288
+ t.Errorf("%s: wrong result value lines: %#q", desc.label, lines)
289
+ } else {
290
+ t.Errorf("%s: wrong result: %#v", desc.label, resp)
291
+ }
292
+ } else if err == nil {
293
+ t.Errorf("%s: got no error, want error %q", desc.label, *desc.errContains)
294
+ } else if code := grpcStatus.Code(err); code != desc.errCode {
295
+ t.Errorf("%s: got error code %q, want %q", desc.label, code, desc.errCode)
296
+ } else if desc.errContains != nil && !strings.Contains(err.Error(), *desc.errContains) {
297
+ t.Errorf("%s: got error %v, want error %q", desc.label, err, *desc.errContains)
298
+ }
299
+ }
300
+ }
@@ -31,7 +31,7 @@ func makeTestKit() testKit {
31
31
 
32
32
  db := dbm.NewMemDB()
33
33
  ms := store.NewCommitMultiStore(db)
34
- ms.MountStoreWithDB(vstorageStoreKey, sdk.StoreTypeIAVL, db)
34
+ ms.MountStoreWithDB(vstorageStoreKey, storetypes.StoreTypeIAVL, db)
35
35
  err := ms.LoadLatestVersion()
36
36
  if err != nil {
37
37
  panic(err)
@@ -33,7 +33,11 @@ func getVstorageEntryPath(urlPathSegments []string) (string, error) {
33
33
  // be used to extend it to a vstorage path such as "foo.bar.baz").
34
34
  func NewQuerier(keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier {
35
35
  return func(ctx sdk.Context, urlPathSegments []string, req abci.RequestQuery) (res []byte, err error) {
36
- switch urlPathSegments[0] {
36
+ var queryType string
37
+ if len(urlPathSegments) > 0 {
38
+ queryType = urlPathSegments[0]
39
+ }
40
+ switch queryType {
37
41
  case QueryData:
38
42
  entryPath, entryPathErr := getVstorageEntryPath(urlPathSegments[1:])
39
43
  if entryPathErr != nil {
@@ -47,7 +51,7 @@ func NewQuerier(keeper Keeper, legacyQuerierCdc *codec.LegacyAmino) sdk.Querier
47
51
  }
48
52
  return queryChildren(ctx, entryPath, req, keeper, legacyQuerierCdc)
49
53
  default:
50
- return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown vstorage query endpoint")
54
+ return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown vstorage query path")
51
55
  }
52
56
  }
53
57
  }