@agoric/cosmos 0.35.0-u13.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.
- package/CHANGELOG.md +43 -0
- package/Makefile +25 -12
- package/ante/ante.go +7 -5
- package/app/app.go +118 -86
- package/app/export.go +13 -6
- package/cmd/agd/main.go +5 -3
- package/cmd/libdaemon/main.go +5 -2
- package/daemon/cmd/genaccounts.go +13 -9
- package/daemon/cmd/root.go +38 -15
- package/daemon/cmd/root_test.go +1 -1
- package/daemon/cmd/testnet.go +17 -6
- package/daemon/main.go +3 -2
- package/git-revision.txt +1 -1
- package/go.mod +95 -64
- package/go.sum +592 -243
- package/package.json +3 -3
- package/proto/agoric/vstorage/query.proto +53 -1
- package/scripts/protocgen.sh +12 -1
- package/third_party/proto/buf.yaml +1 -0
- package/third_party/proto/cosmos/base/query/v1beta1/pagination.proto +4 -1
- package/third_party/proto/cosmos/base/v1beta1/coin.proto +7 -4
- package/third_party/proto/cosmos/upgrade/v1beta1/upgrade.proto +16 -6
- package/third_party/proto/cosmos_proto/cosmos.proto +97 -0
- package/third_party/proto/google/api/annotations.proto +1 -1
- package/third_party/proto/google/api/http.proto +181 -120
- package/third_party/proto/google/api/httpbody.proto +9 -6
- package/third_party/proto/google/protobuf/any.proto +1 -7
- package/third_party/proto/ibc/core/channel/v1/channel.proto +15 -1
- package/third_party/proto/ibc/core/client/v1/client.proto +9 -6
- package/upgradegaia.sh +13 -4
- package/vm/action.go +133 -0
- package/vm/action_test.go +129 -0
- package/vm/controller.go +9 -16
- package/vm/core_proposals.go +31 -0
- package/x/lien/keeper/account.go +3 -3
- package/x/lien/keeper/keeper.go +5 -4
- package/x/lien/keeper/keeper_test.go +9 -9
- package/x/lien/lien.go +6 -4
- package/x/lien/lien_test.go +20 -16
- package/x/swingset/abci.go +25 -33
- package/x/swingset/client/cli/query.go +2 -2
- package/x/swingset/client/cli/tx.go +48 -33
- package/x/swingset/client/proposal_handler.go +2 -17
- package/x/swingset/keeper/keeper.go +30 -15
- package/x/swingset/keeper/keeper_test.go +1 -1
- package/x/swingset/keeper/msg_server.go +21 -51
- package/x/swingset/keeper/proposal.go +14 -8
- package/x/swingset/keeper/querier.go +14 -6
- package/x/swingset/keeper/swing_store_exports_handler.go +5 -1
- package/x/swingset/proposal_handler.go +3 -3
- package/x/swingset/swingset.go +4 -2
- package/x/swingset/types/codec.go +2 -2
- package/x/swingset/types/msgs.pb.go +16 -16
- package/x/swingset/types/proposal.go +5 -5
- package/x/swingset/types/types.go +30 -28
- package/x/vbank/keeper/keeper.go +3 -2
- package/x/vbank/keeper/querier.go +6 -2
- package/x/vbank/keeper/rewards.go +1 -1
- package/x/vbank/vbank.go +19 -17
- package/x/vbank/vbank_test.go +18 -18
- package/x/vibc/handler.go +3 -8
- package/x/vibc/ibc.go +66 -113
- package/x/vibc/keeper/keeper.go +19 -18
- package/x/vibc/types/expected_keepers.go +13 -5
- package/x/vibc/types/msgs.go +1 -1
- package/x/vibc/types/msgs.pb.go +1 -1
- package/x/vstorage/README.md +138 -0
- package/x/vstorage/capdata/capdata.go +298 -0
- package/x/vstorage/capdata/capdata_test.go +352 -0
- package/x/vstorage/client/cli/query.go +51 -4
- package/x/vstorage/keeper/grpc_query.go +221 -0
- package/x/vstorage/keeper/keeper.go +3 -2
- package/x/vstorage/keeper/keeper_grpc_test.go +300 -0
- package/x/vstorage/keeper/keeper_test.go +1 -1
- package/x/vstorage/keeper/querier.go +6 -2
- package/x/vstorage/types/query.pb.go +646 -36
- package/x/vstorage/types/query.pb.gw.go +119 -0
- package/x/vstorage/vstorage.go +16 -15
- package/x/vstorage/vstorage_test.go +5 -5
- /package/{src/index.cjs → index.cjs} +0 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
package capdata
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"fmt"
|
|
6
|
+
"reflect"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
func ptr[T any](v T) *T {
|
|
12
|
+
return &v
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
func mustJsonMarshal(val any) string {
|
|
16
|
+
jsonText, err := JsonMarshal(val)
|
|
17
|
+
if err != nil {
|
|
18
|
+
panic(err)
|
|
19
|
+
}
|
|
20
|
+
return string(jsonText)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func mustJsonUnmarshal(jsonText string, ptr any) {
|
|
24
|
+
if err := json.Unmarshal([]byte(jsonText), ptr); err != nil {
|
|
25
|
+
panic(err)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func prefixBigint(bigint *CapdataBigint) interface{} {
|
|
30
|
+
return fmt.Sprintf("bigint:%s", bigint.Normalized)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func remotableToString(r *CapdataRemotable) interface{} {
|
|
34
|
+
iface := ""
|
|
35
|
+
if r.Iface != nil {
|
|
36
|
+
iface = *r.Iface
|
|
37
|
+
}
|
|
38
|
+
return fmt.Sprintf("remotable:%s{%s}", iface, r.Id)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func Test_JsonMarshal(t *testing.T) {
|
|
42
|
+
type testCase struct {
|
|
43
|
+
input string
|
|
44
|
+
expected string
|
|
45
|
+
errContains *string
|
|
46
|
+
}
|
|
47
|
+
testCases := []testCase{
|
|
48
|
+
{
|
|
49
|
+
input: "<>&\u2028\u2029",
|
|
50
|
+
expected: `"<>&\u2028\u2029"`,
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
for _, desc := range testCases {
|
|
54
|
+
label := fmt.Sprintf("%q", desc.input)
|
|
55
|
+
result, err := JsonMarshal(desc.input)
|
|
56
|
+
if desc.errContains == nil {
|
|
57
|
+
if err != nil {
|
|
58
|
+
t.Errorf("%s: got unexpected error %v", label, err)
|
|
59
|
+
} else if string(result) != desc.expected {
|
|
60
|
+
t.Errorf("%s: wrong result: %#q", label, result)
|
|
61
|
+
}
|
|
62
|
+
} else if err == nil {
|
|
63
|
+
t.Errorf("%s: got no error, want error %q", label, *desc.errContains)
|
|
64
|
+
} else if !strings.Contains(err.Error(), *desc.errContains) {
|
|
65
|
+
t.Errorf("%s: got error %v, want error %q", label, err, *desc.errContains)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func Test_DecodeSerializedCapdata(t *testing.T) {
|
|
71
|
+
type testCase struct {
|
|
72
|
+
format string
|
|
73
|
+
label string
|
|
74
|
+
body string
|
|
75
|
+
slots []interface{}
|
|
76
|
+
expected string
|
|
77
|
+
errContains *string
|
|
78
|
+
transformations CapdataValueTransformations
|
|
79
|
+
}
|
|
80
|
+
testCases := []testCase{
|
|
81
|
+
// JSON
|
|
82
|
+
// cf. https://github.com/endojs/endo/blob/209b612e0a267239f33cd607e94ccd179d3ba248/packages/marshal/test/test-marshal-capdata.js#L11
|
|
83
|
+
{body: `[1, 2]`},
|
|
84
|
+
{body: `{ "foo": 1 }`},
|
|
85
|
+
{body: `{}`},
|
|
86
|
+
{body: `{ "a": 1, "b": 2 }`},
|
|
87
|
+
{body: `{ "a": 1, "b": { "c": 3 } }`},
|
|
88
|
+
{body: `true`},
|
|
89
|
+
{body: `1`},
|
|
90
|
+
{body: `"abc"`},
|
|
91
|
+
{body: `null`},
|
|
92
|
+
|
|
93
|
+
// transformation of non-JSON values
|
|
94
|
+
{format: "smallcaps", label: "bigint",
|
|
95
|
+
body: `"+98765432101234567890"`,
|
|
96
|
+
expected: `"bigint:98765432101234567890"`,
|
|
97
|
+
transformations: CapdataValueTransformations{Bigint: prefixBigint},
|
|
98
|
+
},
|
|
99
|
+
{format: "legacy", label: "bigint",
|
|
100
|
+
body: `{ "@qclass": "bigint", "digits": "98765432101234567890" }`,
|
|
101
|
+
expected: `"bigint:98765432101234567890"`,
|
|
102
|
+
transformations: CapdataValueTransformations{Bigint: prefixBigint},
|
|
103
|
+
},
|
|
104
|
+
{format: "smallcaps", label: "remotables",
|
|
105
|
+
body: `["$0.Foo", "$0"]`,
|
|
106
|
+
slots: []interface{}{"a"},
|
|
107
|
+
expected: `["remotable:Foo{a}","remotable:Foo{a}"]`,
|
|
108
|
+
transformations: CapdataValueTransformations{Remotable: remotableToString},
|
|
109
|
+
},
|
|
110
|
+
{format: "legacy", label: "remotables",
|
|
111
|
+
body: `[{"@qclass":"slot","index":0,"iface":"Foo"}, {"@qclass":"slot","index":0}]`,
|
|
112
|
+
slots: []interface{}{"a"},
|
|
113
|
+
expected: `["remotable:Foo{a}","remotable:Foo{a}"]`,
|
|
114
|
+
transformations: CapdataValueTransformations{Remotable: remotableToString},
|
|
115
|
+
},
|
|
116
|
+
{format: "smallcaps", label: "escaped string",
|
|
117
|
+
body: `"!#escaped"`,
|
|
118
|
+
expected: `"#escaped"`,
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// unimplemented
|
|
122
|
+
{format: "smallcaps",
|
|
123
|
+
body: `"#undefined"`,
|
|
124
|
+
errContains: ptr("not implemented"),
|
|
125
|
+
},
|
|
126
|
+
{format: "legacy", label: "undefined",
|
|
127
|
+
body: `{"@qclass":"undefined"}`,
|
|
128
|
+
errContains: ptr("not implemented"),
|
|
129
|
+
},
|
|
130
|
+
{format: "smallcaps",
|
|
131
|
+
body: `"#NaN"`,
|
|
132
|
+
errContains: ptr("not implemented"),
|
|
133
|
+
},
|
|
134
|
+
{format: "legacy", label: "NaN",
|
|
135
|
+
body: `{"@qclass":"NaN"}`,
|
|
136
|
+
errContains: ptr("not implemented"),
|
|
137
|
+
},
|
|
138
|
+
{format: "smallcaps",
|
|
139
|
+
body: `"#Infinity"`,
|
|
140
|
+
errContains: ptr("not implemented"),
|
|
141
|
+
},
|
|
142
|
+
{format: "legacy", label: "Infinity",
|
|
143
|
+
body: `{"@qclass":"Infinity"}`,
|
|
144
|
+
errContains: ptr("not implemented"),
|
|
145
|
+
},
|
|
146
|
+
{format: "smallcaps",
|
|
147
|
+
body: `"#-Infinity"`,
|
|
148
|
+
errContains: ptr("not implemented"),
|
|
149
|
+
},
|
|
150
|
+
{format: "legacy", label: "-Infinity",
|
|
151
|
+
body: `{"@qclass":"-Infinity"}`,
|
|
152
|
+
errContains: ptr("not implemented"),
|
|
153
|
+
},
|
|
154
|
+
{format: "smallcaps", label: "symbol",
|
|
155
|
+
body: `"%foo"`,
|
|
156
|
+
errContains: ptr("not implemented"),
|
|
157
|
+
},
|
|
158
|
+
{format: "legacy", label: "symbol",
|
|
159
|
+
body: `{"@qclass":"symbol"}`,
|
|
160
|
+
errContains: ptr("not implemented"),
|
|
161
|
+
},
|
|
162
|
+
{format: "smallcaps", label: "tagged",
|
|
163
|
+
body: `{"#tag":"foo","payload":"bar"}`,
|
|
164
|
+
errContains: ptr("not implemented: #tag"),
|
|
165
|
+
},
|
|
166
|
+
{format: "legacy", label: "tagged",
|
|
167
|
+
body: `{"@qclass":"tagged","tag":"foo","payload":"bar"}`,
|
|
168
|
+
errContains: ptr("not implemented"),
|
|
169
|
+
},
|
|
170
|
+
{format: "smallcaps", label: "error",
|
|
171
|
+
body: `{"#error":"","name":"Error"}`,
|
|
172
|
+
errContains: ptr("not implemented: #error"),
|
|
173
|
+
},
|
|
174
|
+
{format: "legacy", label: "error",
|
|
175
|
+
body: `{"@qclass":"error","message":"foo","name":"bar"}`,
|
|
176
|
+
errContains: ptr("not implemented"),
|
|
177
|
+
},
|
|
178
|
+
{format: "smallcaps", label: "promise",
|
|
179
|
+
body: `"&0"`,
|
|
180
|
+
slots: []interface{}{"a"},
|
|
181
|
+
errContains: ptr("not implemented"),
|
|
182
|
+
},
|
|
183
|
+
{format: "legacy", label: "error",
|
|
184
|
+
body: `{"@qclass":"hilbert","original":"foo"}`,
|
|
185
|
+
errContains: ptr("not implemented"),
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
// missing transformations
|
|
189
|
+
{format: "smallcaps", label: "untransformed bigint",
|
|
190
|
+
body: `"+98765432101234567890"`,
|
|
191
|
+
errContains: ptr("untransformed bigint"),
|
|
192
|
+
},
|
|
193
|
+
{format: "legacy", label: "untransformed bigint",
|
|
194
|
+
body: `{ "@qclass": "bigint", "digits": "98765432101234567890" }`,
|
|
195
|
+
errContains: ptr("untransformed bigint"),
|
|
196
|
+
},
|
|
197
|
+
{format: "smallcaps", label: "untransformed remotable",
|
|
198
|
+
body: `["$0.Foo", "$0"]`,
|
|
199
|
+
slots: []interface{}{"a"},
|
|
200
|
+
errContains: ptr("untransformed remotable"),
|
|
201
|
+
},
|
|
202
|
+
{format: "legacy", label: "untransformed remotable",
|
|
203
|
+
body: `[{"@qclass":"slot","index":0,"iface":"Foo"}, {"@qclass":"slot","index":0}]`,
|
|
204
|
+
slots: []interface{}{"a"},
|
|
205
|
+
errContains: ptr("untransformed remotable"),
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// invalid data
|
|
209
|
+
{format: "smallcaps", label: "iface mismatch",
|
|
210
|
+
body: `["$0.Foo", "$0."]`,
|
|
211
|
+
slots: []interface{}{"a"},
|
|
212
|
+
errContains: ptr("iface mismatch"),
|
|
213
|
+
},
|
|
214
|
+
{format: "legacy", label: "iface mismatch",
|
|
215
|
+
body: `[{"@qclass":"slot","index":0,"iface":"Foo"}, {"@qclass":"slot","index":0,"iface":""}]`,
|
|
216
|
+
slots: []interface{}{"a"},
|
|
217
|
+
errContains: ptr("iface mismatch"),
|
|
218
|
+
},
|
|
219
|
+
{format: "smallcaps", label: "invalid slot index (out of bounds)",
|
|
220
|
+
body: `"$0.Foo"`,
|
|
221
|
+
errContains: ptr("invalid slot index"),
|
|
222
|
+
},
|
|
223
|
+
{format: "legacy", label: "invalid slot index (out of bounds)",
|
|
224
|
+
body: `{"@qclass":"slot","index":0}`,
|
|
225
|
+
errContains: ptr("invalid slot index"),
|
|
226
|
+
},
|
|
227
|
+
{format: "smallcaps", label: "invalid slot index (bad format)",
|
|
228
|
+
body: `"$x.Foo"`,
|
|
229
|
+
slots: []interface{}{"a"},
|
|
230
|
+
errContains: ptr("invalid slot index"),
|
|
231
|
+
},
|
|
232
|
+
{format: "legacy", label: "invalid slot index (missing)",
|
|
233
|
+
body: `{"@qclass":"slot"}`,
|
|
234
|
+
slots: []interface{}{"a"},
|
|
235
|
+
errContains: ptr("invalid slot index"),
|
|
236
|
+
},
|
|
237
|
+
{format: "legacy", label: "invalid slot index (null)",
|
|
238
|
+
body: `{"@qclass":"slot","index":null}`,
|
|
239
|
+
slots: []interface{}{"a"},
|
|
240
|
+
errContains: ptr("invalid slot index"),
|
|
241
|
+
},
|
|
242
|
+
{format: "legacy", label: "invalid slot index (string)",
|
|
243
|
+
body: `{"@qclass":"slot","index":"0"}`,
|
|
244
|
+
slots: []interface{}{"a"},
|
|
245
|
+
errContains: ptr("invalid slot index"),
|
|
246
|
+
},
|
|
247
|
+
{format: "legacy", label: "invalid slot index (non-integer)",
|
|
248
|
+
body: `{"@qclass":"slot","index":0.1}`,
|
|
249
|
+
slots: []interface{}{"a"},
|
|
250
|
+
errContains: ptr("invalid slot index"),
|
|
251
|
+
},
|
|
252
|
+
{format: "legacy", label: "invalid slot index (negative)",
|
|
253
|
+
body: `{"@qclass":"slot","index":-1}`,
|
|
254
|
+
slots: []interface{}{"a"},
|
|
255
|
+
errContains: ptr("invalid slot index"),
|
|
256
|
+
},
|
|
257
|
+
{format: "legacy", label: "invalid slot iface (number)",
|
|
258
|
+
body: `{"@qclass":"slot","index":0,"iface":0}`,
|
|
259
|
+
slots: []interface{}{"a"},
|
|
260
|
+
errContains: ptr("invalid slot iface"),
|
|
261
|
+
},
|
|
262
|
+
{format: "smallcaps", label: "unrecognized record type",
|
|
263
|
+
body: `{"#foo":0}`,
|
|
264
|
+
errContains: ptr("unrecognized record type"),
|
|
265
|
+
},
|
|
266
|
+
{format: "legacy", label: "invalid @qclass (null)",
|
|
267
|
+
body: `{"@qclass":null}`,
|
|
268
|
+
errContains: ptr("invalid @qclass"),
|
|
269
|
+
},
|
|
270
|
+
{format: "legacy", label: "invalid @qclass (number)",
|
|
271
|
+
body: `{"@qclass":1}`,
|
|
272
|
+
errContains: ptr("invalid @qclass"),
|
|
273
|
+
},
|
|
274
|
+
{format: "legacy", label: "invalid @qclass (number)",
|
|
275
|
+
body: `{"@qclass":1}`,
|
|
276
|
+
errContains: ptr("invalid @qclass"),
|
|
277
|
+
},
|
|
278
|
+
{format: "legacy", label: "unrecognized @qclass",
|
|
279
|
+
body: `{"@qclass":"foo"}`,
|
|
280
|
+
errContains: ptr("unrecognized @qclass"),
|
|
281
|
+
},
|
|
282
|
+
{format: "smallcaps", label: "invalid copyRecord key",
|
|
283
|
+
body: `{"+0":0}`,
|
|
284
|
+
errContains: ptr("invalid copyRecord key"),
|
|
285
|
+
},
|
|
286
|
+
{format: "smallcaps", label: "invalid bigint (`--`)",
|
|
287
|
+
body: `"--"`,
|
|
288
|
+
errContains: ptr("invalid bigint"),
|
|
289
|
+
},
|
|
290
|
+
{format: "smallcaps", label: "invalid bigint (`+0x`)",
|
|
291
|
+
body: `"+0x"`,
|
|
292
|
+
errContains: ptr("invalid bigint"),
|
|
293
|
+
},
|
|
294
|
+
{format: "legacy", label: "invalid bigint (no digits)",
|
|
295
|
+
body: `{"@qclass":"bigint"}`,
|
|
296
|
+
errContains: ptr("invalid bigint"),
|
|
297
|
+
},
|
|
298
|
+
{format: "legacy", label: "invalid bigint (null digits)",
|
|
299
|
+
body: `{"@qclass":"bigint","digits":null}`,
|
|
300
|
+
errContains: ptr("invalid bigint"),
|
|
301
|
+
},
|
|
302
|
+
{format: "legacy", label: "invalid bigint (`7up`)",
|
|
303
|
+
body: `{"@qclass":"bigint","digits":"7up"}`,
|
|
304
|
+
errContains: ptr("invalid bigint"),
|
|
305
|
+
},
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
for _, desc := range testCases {
|
|
309
|
+
slots := desc.slots
|
|
310
|
+
if slots == nil {
|
|
311
|
+
slots = []interface{}{}
|
|
312
|
+
}
|
|
313
|
+
var expected interface{}
|
|
314
|
+
if desc.expected != "" {
|
|
315
|
+
mustJsonUnmarshal(desc.expected, &expected)
|
|
316
|
+
} else {
|
|
317
|
+
mustJsonUnmarshal(desc.body, &expected)
|
|
318
|
+
}
|
|
319
|
+
for _, format := range []string{"smallcaps", "legacy"} {
|
|
320
|
+
if desc.format != "" && desc.format != format {
|
|
321
|
+
continue
|
|
322
|
+
}
|
|
323
|
+
label := fmt.Sprintf("%s %s", format, desc.label)
|
|
324
|
+
if desc.label == "" {
|
|
325
|
+
label = fmt.Sprintf("%s %s", format, desc.body)
|
|
326
|
+
}
|
|
327
|
+
capdata := Capdata{desc.body, slots}
|
|
328
|
+
if format == "smallcaps" {
|
|
329
|
+
capdata.Body = "#" + capdata.Body
|
|
330
|
+
}
|
|
331
|
+
intermediate, err := DecodeSerializedCapdata(mustJsonMarshal(capdata), desc.transformations)
|
|
332
|
+
// Replace each Remotable with its representation before comparing.
|
|
333
|
+
var got interface{}
|
|
334
|
+
mustJsonUnmarshal(mustJsonMarshal(intermediate), &got)
|
|
335
|
+
if desc.errContains == nil {
|
|
336
|
+
if err != nil {
|
|
337
|
+
t.Errorf("%s: got unexpected error %v", label, err)
|
|
338
|
+
} else if !reflect.DeepEqual(got, expected) {
|
|
339
|
+
result, err := JsonMarshal(got)
|
|
340
|
+
if err != nil {
|
|
341
|
+
panic(fmt.Errorf("%s: %v", label, err))
|
|
342
|
+
}
|
|
343
|
+
t.Errorf("%s: wrong result: %s", label, result)
|
|
344
|
+
}
|
|
345
|
+
} else if err == nil {
|
|
346
|
+
t.Errorf("%s: got no error, want error %q", label, *desc.errContains)
|
|
347
|
+
} else if !strings.Contains(err.Error(), *desc.errContains) {
|
|
348
|
+
t.Errorf("%s: got error %v, want error %q", label, err, *desc.errContains)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
@@ -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
|
|
30
|
-
Short: "get
|
|
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
|
|
62
|
-
|
|
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")
|