@agoric/cosmos 0.34.2-dev-ecf2d8e.0 → 0.35.0-other-dev-70beeb7.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 (40) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/app/app.go +103 -14
  3. package/app/const.go +6 -0
  4. package/cmd/agd/main.go +11 -133
  5. package/cmd/libdaemon/main.go +53 -67
  6. package/daemon/cmd/root.go +24 -55
  7. package/daemon/main.go +1 -3
  8. package/git-revision.txt +1 -1
  9. package/package.json +3 -3
  10. package/proto/agoric/vstorage/query.proto +1 -53
  11. package/vm/controller.go +18 -8
  12. package/x/lien/lien.go +4 -6
  13. package/x/lien/lien_test.go +15 -19
  14. package/x/swingset/abci.go +2 -3
  15. package/x/swingset/swingset.go +2 -4
  16. package/x/swingset/types/default-params.go +1 -1
  17. package/x/swingset/types/msgs.pb.go +16 -16
  18. package/x/vbank/vbank.go +10 -11
  19. package/x/vbank/vbank_test.go +5 -5
  20. package/x/vibc/ibc.go +11 -12
  21. package/x/vibc/keeper/keeper.go +15 -1
  22. package/x/vibc/types/expected_keepers.go +1 -2
  23. package/x/vstorage/keeper/grpc_query.go +0 -221
  24. package/x/vstorage/keeper/querier.go +11 -31
  25. package/x/vstorage/types/path_keys.go +10 -22
  26. package/x/vstorage/types/path_keys_test.go +18 -84
  27. package/x/vstorage/types/query.pb.go +36 -646
  28. package/x/vstorage/types/query.pb.gw.go +0 -119
  29. package/x/vstorage/vstorage.go +15 -16
  30. package/x/vstorage/vstorage_test.go +4 -4
  31. package/cmd/agd/agvm.go +0 -42
  32. package/vm/client.go +0 -113
  33. package/vm/client_test.go +0 -184
  34. package/vm/jsonrpcconn/jsonrpcconn.go +0 -160
  35. package/vm/jsonrpcconn/jsonrpcconn_test.go +0 -126
  36. package/vm/server.go +0 -23
  37. package/x/vstorage/README.md +0 -95
  38. package/x/vstorage/capdata/capdata.go +0 -298
  39. package/x/vstorage/capdata/capdata_test.go +0 -352
  40. package/x/vstorage/keeper/keeper_grpc_test.go +0 -300
@@ -1,160 +0,0 @@
1
- /*
2
- Package jsonrpcconn provides a way to multiplex an io stream into two
3
- streams where incoming JSON-RPC requests go to one stream and incoming
4
- JSON-RPC responses go to another. Outputs are merged.
5
-
6
- The JSON-RPCv1 protocol is peer-to-peer, but the implementation in the Go
7
- standard library only supports client-server. By multiplexing a single
8
- io.ReadWriteCloser stream into separate server (receives requests) and
9
- client (receives responses) streams, two RPC halves can share the same
10
- underlying connection.
11
- */
12
- package jsonrpcconn
13
-
14
- import (
15
- "encoding/json"
16
- "io"
17
- )
18
-
19
- // jsonRpcMsg can unmarshal either a JSON-RPC
20
- // request or response object.
21
- type jsonRpcMsg struct {
22
- Error json.RawMessage `json:"error"`
23
- Id json.RawMessage `json:"id"`
24
- Method *string `json:"method"`
25
- Params []json.RawMessage `json:"params"`
26
- Result json.RawMessage `json:"result"`
27
- }
28
-
29
- // mux holds the underlying connection and the pipe reader/writer
30
- // pairs for the server (request) and client (response) sides.
31
- // Any protocol error or closing any channel will cause shutdown.
32
- type mux struct {
33
- conn io.ReadWriteCloser
34
- reqReader *io.PipeReader
35
- reqWriter *io.PipeWriter
36
- respReader *io.PipeReader
37
- respWriter *io.PipeWriter
38
- }
39
-
40
- func newMux(conn io.ReadWriteCloser) mux {
41
- reqReader, reqWriter := io.Pipe()
42
- respReader, respWriter := io.Pipe()
43
- m := mux{
44
- conn: conn,
45
- reqReader: reqReader,
46
- reqWriter: reqWriter,
47
- respReader: respReader,
48
- respWriter: respWriter,
49
- }
50
- go m.input()
51
- return m
52
- }
53
-
54
- func (m mux) input() {
55
- var err error
56
- dec := json.NewDecoder(m.conn)
57
- for {
58
- // read the next JSON value, preserve its wire format
59
- var raw json.RawMessage
60
- err = dec.Decode(&raw)
61
- if err != nil {
62
- break
63
- }
64
-
65
- // parse as JSON-RPC
66
- var msg jsonRpcMsg
67
- err = json.Unmarshal(raw, &msg)
68
- if err != nil {
69
- break
70
- }
71
-
72
- // send to one of the outputs
73
- if msg.Method != nil {
74
- // presume a request, the consumer will handle any missing fields
75
- _, err = m.reqWriter.Write(raw)
76
- } else {
77
- // presume a response, the consumer will handle any missing fields
78
- _, err = m.respWriter.Write(raw)
79
- }
80
- if err != nil {
81
- break
82
- }
83
- }
84
- m.reqWriter.CloseWithError(err)
85
- m.respWriter.CloseWithError(err)
86
- m.conn.Close()
87
- }
88
-
89
- // clientChan is a view of the mux for the client channel.
90
- type clientChan mux
91
-
92
- // Close implements the io.Closer interface.
93
- func (c clientChan) Close() error {
94
- return c.conn.Close()
95
- }
96
-
97
- // Read implements the io.Reader interface.
98
- func (c clientChan) Read(p []byte) (int, error) {
99
- return c.respReader.Read(p)
100
- }
101
-
102
- // Write implements the io.Writer interface.
103
- func (c clientChan) Write(p []byte) (int, error) {
104
- return c.conn.Write(p)
105
- }
106
-
107
- // serverChan is a view of the mux for the server channel.
108
- type serverChan mux
109
-
110
- // Close implements the io.Closer interface.
111
- func (s serverChan) Close() error {
112
- return s.conn.Close()
113
- }
114
-
115
- // Read implements the io.Reader interface.
116
- func (s serverChan) Read(p []byte) (int, error) {
117
- return s.reqReader.Read(p)
118
- }
119
-
120
- // Write implements the io.Writer interface.
121
- func (s serverChan) Write(p []byte) (int, error) {
122
- return s.conn.Write(p)
123
- }
124
-
125
- // ClientServerConn multiplexes an input/output stream for the JSON-RPCv1
126
- // protocol into streams specific for client traffic and server traffic.
127
- // Full JSON objects must be written atomically to either stream to
128
- // interleave correctly.
129
- func ClientServerConn(conn io.ReadWriteCloser) (clientConn io.ReadWriteCloser, serverConn io.ReadWriteCloser) {
130
- m := newMux(conn)
131
- clientConn = clientChan(m)
132
- serverConn = serverChan(m)
133
- return
134
- }
135
-
136
- type conn struct {
137
- rd io.ReadCloser
138
- wr io.WriteCloser
139
- }
140
-
141
- // Close implments the io.Closer interface.
142
- func (e conn) Close() error {
143
- e.rd.Close()
144
- return e.wr.Close()
145
- }
146
-
147
- // Read implements the io.Reader interface.
148
- func (e conn) Read(p []byte) (int, error) {
149
- return e.rd.Read(p)
150
- }
151
-
152
- // Write implements the io.Writer interface.
153
- func (e conn) Write(p []byte) (int, error) {
154
- return e.wr.Write(p)
155
- }
156
-
157
- // NewConn returns a connection from a reader and a writer.
158
- func NewConn(rd io.ReadCloser, wr io.WriteCloser) io.ReadWriteCloser {
159
- return conn{rd: rd, wr: wr}
160
- }
@@ -1,126 +0,0 @@
1
- package jsonrpcconn_test
2
-
3
- import (
4
- "fmt"
5
- "net"
6
- "net/rpc"
7
- "net/rpc/jsonrpc"
8
- "testing"
9
-
10
- "github.com/Agoric/agoric-sdk/golang/cosmos/vm/jsonrpcconn"
11
- )
12
-
13
- /*
14
- type testConn struct {
15
- input *bytes.Buffer
16
- output *bytes.Buffer
17
- done chan struct{}
18
- }
19
-
20
- func newTestConn(input string) io.ReadWriteCloser {
21
- return testConn{
22
- input: bytes.NewBufferString(input),
23
- output: new(bytes.Buffer),
24
- done: make(chan struct{}),
25
- }
26
- }
27
-
28
- func (tc testConn) Read(p []byte) (int, error) {
29
- n, err := tc.input.Read(p)
30
- if err == io.EOF {
31
- <-tc.done
32
- }
33
- return n, err
34
- }
35
-
36
- func (tc testConn) Write(p []byte) (int, error) {
37
- return tc.output.Write(p)
38
- }
39
-
40
- func (tc testConn) Close() error {
41
- close(tc.done)
42
- return nil
43
- }
44
-
45
- func TestMux(t *testing.T) {
46
- input := `{"id": 1, "method": "foo", "params": [1, 2]}
47
- {"id": "aaa", "result": true, "error": null}
48
- {"id": null, "method": "bar", "params": ["string", 3, false]}`
49
- tc := newTestConn(input)
50
- client, server := jsonrpcconn.ClientServerConn(tc)
51
- client.Read
52
- var wg sync.WaitGroup
53
- wg.Add(2)
54
- go func() {
55
-
56
- }()
57
- wg.Wait()
58
- } */
59
-
60
- type Args struct {
61
- A, B int
62
- }
63
-
64
- type Arith struct{}
65
-
66
- func (a *Arith) Add(args Args, reply *int) error {
67
- *reply = args.A + args.B
68
- return nil
69
- }
70
-
71
- func (a *Arith) Mul(args Args, reply *int) error {
72
- *reply = args.A * args.B
73
- return nil
74
- }
75
-
76
- func (a *Arith) Oops(args Args, reply *int) error {
77
- return fmt.Errorf("oops")
78
- }
79
-
80
- func TestJsonRPC(t *testing.T) {
81
- left, right := net.Pipe()
82
- leftClientConn, leftServerConn := jsonrpcconn.ClientServerConn(left)
83
- rightClientConn, rightServerConn := jsonrpcconn.ClientServerConn(right)
84
-
85
- leftClient := jsonrpc.NewClient(leftClientConn)
86
- leftServer := rpc.NewServer()
87
- err := leftServer.RegisterName("foo", new(Arith))
88
- if err != nil {
89
- t.Fatal(err)
90
- }
91
- go leftServer.ServeCodec(jsonrpc.NewServerCodec(leftServerConn))
92
-
93
- rightClient := jsonrpc.NewClient(rightClientConn)
94
- rightServer := rpc.NewServer()
95
- err = rightServer.RegisterName("bar", new(Arith))
96
- if err != nil {
97
- t.Fatal(err)
98
- }
99
- go rightServer.ServeCodec(jsonrpc.NewServerCodec(rightServerConn))
100
-
101
- var reply int
102
- err = leftClient.Call("bar.Add", Args{1, 2}, &reply)
103
- if err != nil {
104
- t.Error(err)
105
- }
106
- if reply != 3 {
107
- t.Errorf("bar.Add want 3, got %d", reply)
108
- }
109
-
110
- err = rightClient.Call("foo.Mul", Args{2, 3}, &reply)
111
- if err != nil {
112
- t.Error(err)
113
- }
114
- if reply != 6 {
115
- t.Errorf("foo.Mul want 6, got %d", reply)
116
- }
117
-
118
- err = leftClient.Call("bar.Oops", Args{7, 11}, &reply)
119
- if err == nil {
120
- t.Errorf("bar.Oops want error, got reply %d", reply)
121
- } else if err.Error() != "oops" {
122
- t.Errorf(`bar.Oops want error "oops", got "%s"`, err.Error())
123
- }
124
- leftClient.Close()
125
- rightClient.Close()
126
- }
package/vm/server.go DELETED
@@ -1,23 +0,0 @@
1
- package vm
2
-
3
- import (
4
- "fmt"
5
- )
6
-
7
- type AgdServer struct {}
8
-
9
- func NewAgdServer() *AgdServer {
10
- return &AgdServer{}
11
- }
12
-
13
- // ReceiveMessage is the method the VM calls in order to have agd receive a
14
- // Message.
15
- func (s AgdServer) ReceiveMessage(msg *Message, reply *string) error {
16
- handler := portToHandler[msg.Port]
17
- if handler == nil {
18
- return fmt.Errorf("unregistered port %d", msg.Port)
19
- }
20
- resp, err := handler.Receive(controllerContext, msg.Data)
21
- *reply = resp
22
- return err
23
- }
@@ -1,95 +0,0 @@
1
- # Virtual Storage
2
-
3
- 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).
4
-
5
- ## Internal Go interface
6
-
7
- [Keeper](./keeper/keeper.go)
8
- * generic
9
- * GetChildren
10
- * GetEntry
11
- * HasEntry
12
- * HasStorage
13
- * SetStorage[AndNotify]
14
- * StreamCell-oriented (a StreamCell captures a block height and an array of values)
15
- * AppendStorageValue[AndNotify]
16
- * queue-oriented (a queue stores items at paths like "$prefix.$n", documenting
17
- the n for the next item to be consumed at "$prefix.head" and the n for the next
18
- next item to be pushed at "$prefix.tail" such that the queue is empty when both
19
- head and tail store the same n)
20
- * GetQueueLength
21
- * PushQueueItem
22
-
23
- ## Internal JSON interface
24
-
25
- This is used by the SwingSet "bridge".
26
-
27
- [Receive](./vstorage.go) with input `{ "method": "...", "args": [...] }`
28
- * generic
29
- * method "entries", args path
30
- * method "get"/"has", args path
31
- * method "set"/"setWithoutNotify", args [[path, value?], ...]
32
- * method "children", args path
33
- * method "values", args path (returns values for children in the same order as method "children")
34
- * method "size", args path (returns the count of children)
35
- * StreamCell-oriented
36
- * method "append", args [[path, value?], ...]
37
-
38
- ## CLI
39
-
40
- A blockchain node may be interrogated by RPC using `agd [--node $url] query vstorage $command` via [client/cli](./client/cli/query.go).
41
- * `children [--height $blockHeight] [-o {text,json}] [$path]`
42
- * `data [--height $blockHeight] [-o {text,json}] $path`
43
-
44
- Examples:
45
- ```sh
46
- $ agd --node https://main.rpc.agoric.net:443/ query vstorage children published.reserve
47
- children:
48
- - governance
49
- - metrics
50
- pagination: null
51
-
52
- $ agd --node https://main.rpc.agoric.net:443/ query vstorage children -o json published.reserve
53
- {"children":["governance","metrics"],"pagination":null}
54
-
55
- $ agd --node https://main.rpc.agoric.net:443/ query vstorage data published.reserve.metrics
56
- value: '{"blockHeight":"11030240","values":["{\"body\":\"#{\\\"allocations\\\":{\\\"Fee\\\":{\\\"brand\\\":\\\"$0.Alleged:
57
- IST brand\\\",\\\"value\\\":\\\"+20053582387\\\"}},\\\"shortfallBalance\\\":{\\\"brand\\\":\\\"$0\\\",\\\"value\\\":\\\"+0\\\"},\\\"totalFeeBurned\\\":{\\\"brand\\\":\\\"$0\\\",\\\"value\\\":\\\"+0\\\"},\\\"totalFeeMinted\\\":{\\\"brand\\\":\\\"$0\\\",\\\"value\\\":\\\"+0\\\"}}\",\"slots\":[\"board0257\"]}"]}'
58
- ```
59
-
60
- ## External protobuf interface
61
-
62
- RPC via [Querier](./keeper/grpc_query.go),
63
- and [CometBFT method "abci_query"](https://docs.cometbft.com/main/rpc/#/ABCI/abci_query)
64
- with params `path` "/agoric.vstorage.Query/..."
65
- and `data` \<serialized protobuf per [vstorage/query.proto](../../proto/agoric/vstorage/query.proto)>
66
- (also via [Querier](./keeper/grpc_query.go))
67
- * /agoric.vstorage.Query/CapData
68
- * /agoric.vstorage.Query/Children
69
- * /agoric.vstorage.Query/Data
70
-
71
- ## External JSON interface
72
-
73
- As described at [Cosmos SDK: Using the REST Endpoints](https://docs.cosmos.network/main/run-node/interact-node#using-the-rest-endpoints), a blockchain node whose [`app.toml` configuration](https://docs.cosmos.network/main/run-node/run-node#configuring-the-node-using-apptoml-and-configtoml) enables the "REST" API server uses [gRPC-Gateway](https://grpc-ecosystem.github.io/grpc-gateway/) and `google.api.http` annotations in [vstorage/query.proto](../../proto/agoric/vstorage/query.proto) to automatically translate the protobuf-based RPC endpoints into URL paths that accept query parameters and emit JSON.
74
- * /agoric/vstorage/capdata/$path?remotableValueFormat={object,string}[&mediaType=JSON%20Lines][&itemFormat=flat]
75
- * /agoric/vstorage/children/$path
76
- * /agoric/vstorage/data/$path
77
-
78
- Example:
79
- ```sh
80
- $ curl -sS 'https://main.api.agoric.net/agoric/vstorage/children/published.committees'
81
- {
82
- "children": [
83
- "Economic_Committee"
84
- ],
85
- "pagination": null
86
- }
87
- ```
88
-
89
- ## Arbitrary-response HTTP interface
90
-
91
- 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)
92
-
93
- [legacy querier](./keeper/querier.go)
94
- * /custom/vstorage/children/$path
95
- * /custom/vstorage/data/$path
@@ -1,298 +0,0 @@
1
- package capdata
2
-
3
- import (
4
- "bytes"
5
- "encoding/json"
6
- "fmt"
7
- "regexp"
8
- "strconv"
9
- "strings"
10
- )
11
-
12
- // JsonMarshal returns JSON text representing its input,
13
- // without special replacement of "<", ">", "&", U+2028, or U+2029.
14
- func JsonMarshal(val any) ([]byte, error) {
15
- buf := &bytes.Buffer{}
16
- encoder := json.NewEncoder(buf)
17
- encoder.SetEscapeHTML(false)
18
- if err := encoder.Encode(val); err != nil {
19
- return nil, err
20
- }
21
- // Return without a trailing line feed.
22
- lineTerminatedJson := buf.Bytes()
23
- return bytes.TrimSuffix(lineTerminatedJson, []byte("\n")), nil
24
- }
25
-
26
- // cf. https://github.com/endojs/endo/tree/master/packages/marshal
27
-
28
- type Capdata struct {
29
- Body string `json:"body"`
30
- Slots []interface{} `json:"slots"`
31
- }
32
-
33
- var validBigint = regexp.MustCompile(`^-?(?:0|[1-9][0-9]*)$`)
34
-
35
- type CapdataBigint struct {
36
- Normalized string
37
- }
38
-
39
- type CapdataRemotable struct {
40
- Id interface{}
41
- Iface *string
42
- Representation interface{}
43
- }
44
-
45
- func NewCapdataBigint(str string) *CapdataBigint {
46
- if !validBigint.MatchString(str) {
47
- return nil
48
- }
49
- bigint := CapdataBigint{str}
50
- return &bigint
51
- }
52
-
53
- func (r *CapdataRemotable) MarshalJSON() ([]byte, error) {
54
- return JsonMarshal(r.Representation)
55
- }
56
-
57
- type CapdataValueTransformations struct {
58
- Bigint func(*CapdataBigint) interface{}
59
- Remotable func(*CapdataRemotable) interface{}
60
- }
61
-
62
- // upsertCapdataRemotable either adds a new CapdataRemotable to `remotables` at the specified
63
- // slot index or updates the iface of the value that is already there, ensuring lack of iface name
64
- // inconsistency (iteration order is not guaranteed to correspond with JSON text like it does in
65
- // JavaScript, so we must accept encountering the "first" reference to a slot late
66
- // and must therefore also defer transformations).
67
- func upsertCapdataRemotable(remotables map[uint64]*CapdataRemotable, slotIndex uint64, id interface{}, iface *string) (*CapdataRemotable, error) {
68
- r := remotables[slotIndex]
69
- if r == nil {
70
- r = new(CapdataRemotable)
71
- r.Id = id
72
- r.Iface = iface
73
- remotables[slotIndex] = r
74
- } else if iface != nil {
75
- if r.Iface != nil && *iface != *r.Iface {
76
- return nil, fmt.Errorf("slot iface mismatch: %q", *iface)
77
- }
78
- r.Iface = iface
79
- }
80
- return r, nil
81
- }
82
-
83
- // decodeCapdataLegacyValue decodes the non-smallcaps encoding of
84
- // https://github.com/endojs/endo/blob/master/packages/marshal/src/encodeToCapData.js
85
- func decodeCapdataLegacyValue(
86
- encoded interface{},
87
- slots []interface{},
88
- remotables map[uint64]*CapdataRemotable,
89
- transformations CapdataValueTransformations,
90
- ) (interface{}, error) {
91
- if arr, ok := encoded.([]interface{}); ok {
92
- for i, v := range arr {
93
- decoded, err := decodeCapdataLegacyValue(v, slots, remotables, transformations)
94
- if err != nil {
95
- return nil, err
96
- }
97
- arr[i] = decoded
98
- }
99
- return arr, nil
100
- } else if obj, ok := encoded.(map[string]interface{}); ok {
101
- if qclassVal, ok := obj["@qclass"]; ok {
102
- qclass, ok := qclassVal.(string)
103
- if !ok {
104
- return nil, fmt.Errorf("invalid @qclass: %q", qclassVal)
105
- }
106
- switch qclass {
107
- case "bigint":
108
- var bigint *CapdataBigint
109
- digitsVal := obj["digits"]
110
- if digitsStr, ok := digitsVal.(string); ok {
111
- bigint = NewCapdataBigint(digitsStr)
112
- }
113
- if bigint == nil {
114
- return nil, fmt.Errorf("invalid bigint: %q", digitsVal)
115
- }
116
- if transformations.Bigint == nil {
117
- return nil, fmt.Errorf("untransformed bigint")
118
- }
119
- return transformations.Bigint(bigint), nil
120
- case "slot":
121
- var iface *string
122
- slotIndexVal, ifaceVal := obj["index"], obj["iface"]
123
- slotIndexNum, ok := slotIndexVal.(float64)
124
- slotIndex := uint64(slotIndexNum)
125
- if !ok || float64(slotIndex) != slotIndexNum || slotIndex >= uint64(len(slots)) {
126
- return nil, fmt.Errorf("invalid slot index: %q", slotIndexVal)
127
- }
128
- if ifaceStr, ok := ifaceVal.(string); ok {
129
- iface = &ifaceStr
130
- } else if ifaceVal != nil {
131
- return nil, fmt.Errorf("invalid slot iface: %q", ifaceVal)
132
- }
133
- return upsertCapdataRemotable(remotables, slotIndex, slots[slotIndex], iface)
134
- case "hilbert":
135
- fallthrough
136
- case "undefined":
137
- fallthrough
138
- case "NaN":
139
- fallthrough
140
- case "Infinity":
141
- fallthrough
142
- case "symbol":
143
- fallthrough
144
- case "tagged":
145
- fallthrough
146
- case "error":
147
- fallthrough
148
- case "-Infinity":
149
- return nil, fmt.Errorf("not implemented: @qclass %q", qclass)
150
- default:
151
- return nil, fmt.Errorf("unrecognized @qclass: %q", qclass)
152
- }
153
- }
154
- for k, v := range obj {
155
- decoded, err := decodeCapdataLegacyValue(v, slots, remotables, transformations)
156
- if err != nil {
157
- return nil, err
158
- }
159
- obj[k] = decoded
160
- }
161
- return obj, nil
162
- } else {
163
- return encoded, nil
164
- }
165
- }
166
-
167
- // decodeCapdataSmallcapsValue decodes the "smallcaps" encoding from
168
- // https://github.com/endojs/endo/blob/master/packages/marshal/src/encodeToSmallcaps.js
169
- func decodeCapdataSmallcapsValue(
170
- encoded interface{},
171
- slots []interface{},
172
- remotables map[uint64]*CapdataRemotable,
173
- transformations CapdataValueTransformations,
174
- ) (interface{}, error) {
175
- if arr, ok := encoded.([]interface{}); ok {
176
- for i, v := range arr {
177
- decoded, err := decodeCapdataSmallcapsValue(v, slots, remotables, transformations)
178
- if err != nil {
179
- return nil, err
180
- }
181
- arr[i] = decoded
182
- }
183
- return arr, nil
184
- } else if encodedObj, ok := encoded.(map[string]interface{}); ok {
185
- if _, ok := encodedObj["#tag"]; ok {
186
- return nil, fmt.Errorf("not implemented: #tag")
187
- }
188
- if _, ok := encodedObj["#error"]; ok {
189
- return nil, fmt.Errorf("not implemented: #error")
190
- }
191
- // We need a distinct output map to avoid reprocessing already-decoded keys.
192
- decodedObj := make(map[string]interface{}, len(encodedObj))
193
- for encodedK, v := range encodedObj {
194
- if strings.HasPrefix(encodedK, "#") {
195
- return nil, fmt.Errorf("unrecognized record type: %q", encodedK)
196
- }
197
- decodedK, err := decodeCapdataSmallcapsValue(encodedK, slots, remotables, CapdataValueTransformations{})
198
- k, ok := decodedK.(string)
199
- if err != nil || !ok {
200
- return nil, fmt.Errorf("invalid copyRecord key: %q", encodedK)
201
- }
202
- decoded, err := decodeCapdataSmallcapsValue(v, slots, remotables, transformations)
203
- if err != nil {
204
- return nil, err
205
- }
206
- decodedObj[k] = decoded
207
- }
208
- return decodedObj, nil
209
- } else if str, ok := encoded.(string); ok {
210
- if len(str) == 0 {
211
- return str, nil
212
- }
213
- switch str[0] {
214
- case '!':
215
- return str[1:], nil
216
- case '+':
217
- // Normalize to no leading "+".
218
- str = str[1:]
219
- fallthrough
220
- case '-':
221
- bigint := NewCapdataBigint(str)
222
- if bigint == nil {
223
- return nil, fmt.Errorf("invalid bigint: %q", encoded.(string))
224
- }
225
- if transformations.Bigint == nil {
226
- return nil, fmt.Errorf("untransformed bigint")
227
- }
228
- return transformations.Bigint(bigint), nil
229
- case '$':
230
- var slotIndexStr string
231
- var iface *string
232
- if dotIndex := strings.IndexByte(str, '.'); dotIndex >= 0 {
233
- slotIndexStr = str[1:dotIndex]
234
- ifaceStr := str[dotIndex+1:]
235
- iface = &ifaceStr
236
- } else {
237
- slotIndexStr = str[1:]
238
- }
239
- slotIndex, err := strconv.ParseUint(slotIndexStr, 10, 0)
240
- if err != nil || slotIndex >= uint64(len(slots)) {
241
- return nil, fmt.Errorf("invalid slot index: %q", str)
242
- }
243
- r, err := upsertCapdataRemotable(remotables, slotIndex, slots[slotIndex], iface)
244
- if err != nil {
245
- return nil, fmt.Errorf("slot iface mismatch: %q", str)
246
- }
247
- return r, nil
248
- case '#':
249
- fallthrough
250
- case '%':
251
- fallthrough
252
- case '&':
253
- return nil, fmt.Errorf("not implemented: %q", str)
254
- default:
255
- if str[0] >= '!' && str[0] <= '-' {
256
- return nil, fmt.Errorf("invalid smallcaps encoding prefix: %q", str[:1])
257
- }
258
- }
259
- return str, nil
260
- } else {
261
- return encoded, nil
262
- }
263
- }
264
-
265
- // DecodeSerializedCapdata accepts JSON text representing encoded CapData and
266
- // decodes it, applying specified transformations for values that otherwise
267
- // hinder interchange.
268
- func DecodeSerializedCapdata(
269
- serializedCapdata string,
270
- transformations CapdataValueTransformations,
271
- ) (interface{}, error) {
272
- var capdata Capdata
273
- if err := json.Unmarshal([]byte(serializedCapdata), &capdata); err != nil {
274
- return nil, err
275
- }
276
- if capdata.Body == "" || capdata.Slots == nil {
277
- return nil, fmt.Errorf("invalid CapData")
278
- }
279
- serializedBody, decodeValue := capdata.Body, decodeCapdataLegacyValue
280
- if strings.HasPrefix(serializedBody, "#") {
281
- serializedBody, decodeValue = serializedBody[1:], decodeCapdataSmallcapsValue
282
- }
283
- var encoded interface{}
284
- if err := json.Unmarshal([]byte(serializedBody), &encoded); err != nil {
285
- return nil, err
286
- }
287
- remotables := map[uint64]*CapdataRemotable{}
288
- decoded, err := decodeValue(encoded, capdata.Slots, remotables, transformations)
289
- if err == nil && len(remotables) > 0 {
290
- if transformations.Remotable == nil {
291
- return nil, fmt.Errorf("untransformed remotable")
292
- }
293
- for _, r := range remotables {
294
- r.Representation = transformations.Remotable(r)
295
- }
296
- }
297
- return decoded, err
298
- }