@agoric/cosmos 0.34.2-dev-5fcf452.0 → 0.34.2-dev-d355030.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/app/app.go +5 -4
- package/cmd/agd/agvm.go +42 -0
- package/cmd/agd/main.go +133 -11
- package/cmd/libdaemon/main.go +67 -53
- package/daemon/cmd/root.go +55 -24
- package/daemon/main.go +3 -1
- package/git-revision.txt +1 -1
- package/package.json +2 -2
- package/vm/client.go +113 -0
- package/vm/client_test.go +184 -0
- package/vm/controller.go +8 -18
- package/vm/jsonrpcconn/jsonrpcconn.go +160 -0
- package/vm/jsonrpcconn/jsonrpcconn_test.go +126 -0
- package/vm/server.go +23 -0
- package/x/lien/lien.go +6 -4
- package/x/lien/lien_test.go +19 -15
- package/x/swingset/abci.go +3 -2
- package/x/swingset/swingset.go +4 -2
- package/x/vbank/vbank.go +11 -10
- package/x/vbank/vbank_test.go +5 -5
- package/x/vibc/ibc.go +11 -9
- package/x/vstorage/vstorage.go +16 -15
- package/x/vstorage/vstorage_test.go +4 -4
package/vm/client.go
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
package vm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
"net/rpc"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// ReceiveMessageMethod is the name of the method we call in order to have the
|
|
10
|
+
// VM receive a Message.
|
|
11
|
+
const ReceiveMessageMethod = "agvm.ReceiveMessage"
|
|
12
|
+
|
|
13
|
+
// Message is what we send to the VM.
|
|
14
|
+
type Message struct {
|
|
15
|
+
Port int
|
|
16
|
+
Data string
|
|
17
|
+
NeedsReply bool
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ClientCodec implements rpc.ClientCodec.
|
|
21
|
+
var _ rpc.ClientCodec = (*ClientCodec)(nil)
|
|
22
|
+
|
|
23
|
+
// ClientCodec implements a net/rpc ClientCodec for the "bridge" between the Go
|
|
24
|
+
// runtime and the VM in the single-process dual-runtime configuration.
|
|
25
|
+
//
|
|
26
|
+
// We expect to call it via the legacy API with signature:
|
|
27
|
+
// sendToController func(needsReply bool, msg string) (string, error)
|
|
28
|
+
// where msg and the returned string are JSON-encoded values.
|
|
29
|
+
//
|
|
30
|
+
// Note that the net/rpc framework cannot express a call that does not expect a
|
|
31
|
+
// response, so we'll note such calls by sending with a reply port of 0 and
|
|
32
|
+
// having the WriteRequest() method fabricate a Receive() call to clear the rpc
|
|
33
|
+
// state.
|
|
34
|
+
type ClientCodec struct {
|
|
35
|
+
ctx context.Context
|
|
36
|
+
send func(port, rPort int, msg string)
|
|
37
|
+
outbound map[int]rpc.Request
|
|
38
|
+
inbound chan *rpc.Response
|
|
39
|
+
replies map[uint64]string
|
|
40
|
+
replyToRead uint64
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// NewClientCodec creates a new ClientCodec.
|
|
44
|
+
func NewClientCodec(ctx context.Context, send func(int, int, string)) *ClientCodec {
|
|
45
|
+
return &ClientCodec{
|
|
46
|
+
ctx: ctx,
|
|
47
|
+
send: send,
|
|
48
|
+
outbound: make(map[int]rpc.Request),
|
|
49
|
+
inbound: make(chan *rpc.Response),
|
|
50
|
+
replies: make(map[uint64]string),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// WriteRequest sends a request to the VM.
|
|
55
|
+
func (cc *ClientCodec) WriteRequest(r *rpc.Request, body interface{}) error {
|
|
56
|
+
if r.ServiceMethod != ReceiveMessageMethod {
|
|
57
|
+
return fmt.Errorf("unknown method %s", r.ServiceMethod)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
msg, ok := body.(Message)
|
|
61
|
+
if !ok {
|
|
62
|
+
return fmt.Errorf("body %T is not a Message", body)
|
|
63
|
+
}
|
|
64
|
+
rPort := int(r.Seq + 1) // rPort is 1-indexed to indicate it's required
|
|
65
|
+
cc.outbound[rPort] = *r
|
|
66
|
+
var senderReplyPort int
|
|
67
|
+
if msg.NeedsReply {
|
|
68
|
+
senderReplyPort = rPort
|
|
69
|
+
}
|
|
70
|
+
cc.send(msg.Port, senderReplyPort, msg.Data)
|
|
71
|
+
if !msg.NeedsReply {
|
|
72
|
+
return cc.Receive(rPort, false, "<no-reply-requested>")
|
|
73
|
+
}
|
|
74
|
+
return nil
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ReadResponseHeader decodes a response header from the VM.
|
|
78
|
+
func (cc *ClientCodec) ReadResponseHeader(r *rpc.Response) error {
|
|
79
|
+
resp := <-cc.inbound
|
|
80
|
+
*r = *resp
|
|
81
|
+
cc.replyToRead = r.Seq
|
|
82
|
+
return nil
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ReadResponseBody decodes a response body (currently just string) from the VM.
|
|
86
|
+
func (cc *ClientCodec) ReadResponseBody(body interface{}) error {
|
|
87
|
+
if body != nil {
|
|
88
|
+
*body.(*string) = cc.replies[cc.replyToRead]
|
|
89
|
+
}
|
|
90
|
+
delete(cc.replies, cc.replyToRead)
|
|
91
|
+
return nil
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Receive is called by the VM to send a response to the client.
|
|
95
|
+
func (cc *ClientCodec) Receive(rPort int, isError bool, data string) error {
|
|
96
|
+
outb := cc.outbound[rPort]
|
|
97
|
+
delete(cc.outbound, rPort)
|
|
98
|
+
resp := &rpc.Response{
|
|
99
|
+
ServiceMethod: outb.ServiceMethod,
|
|
100
|
+
Seq: outb.Seq,
|
|
101
|
+
}
|
|
102
|
+
if isError {
|
|
103
|
+
resp.Error = data
|
|
104
|
+
} else {
|
|
105
|
+
cc.replies[resp.Seq] = data
|
|
106
|
+
}
|
|
107
|
+
cc.inbound <- resp
|
|
108
|
+
return nil
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
func (cc *ClientCodec) Close() error {
|
|
112
|
+
return nil
|
|
113
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
package vm_test
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"fmt"
|
|
7
|
+
"net/rpc"
|
|
8
|
+
"testing"
|
|
9
|
+
"time"
|
|
10
|
+
|
|
11
|
+
"github.com/Agoric/agoric-sdk/golang/cosmos/vm"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
type Sender func(ctx context.Context, needReply bool, str string) (string, error)
|
|
15
|
+
|
|
16
|
+
type errorWrapper struct {
|
|
17
|
+
Error string `json:"error"`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ConnectVMClientCodec creates an RPC client codec and a sender to the
|
|
21
|
+
// in-process implementation of the VM.
|
|
22
|
+
func ConnectVMClientCodec(ctx context.Context, nodePort int, sendFunc func(int, int, string)) (*vm.ClientCodec, Sender) {
|
|
23
|
+
vmClientCodec := vm.NewClientCodec(context.Background(), sendFunc)
|
|
24
|
+
vmClient := rpc.NewClientWithCodec(vmClientCodec)
|
|
25
|
+
|
|
26
|
+
sendToNode := func(ctx context.Context, needReply bool, str string) (string, error) {
|
|
27
|
+
if str == "shutdown" {
|
|
28
|
+
return "", vmClientCodec.Close()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
msg := vm.Message{
|
|
32
|
+
Port: nodePort,
|
|
33
|
+
NeedsReply: needReply,
|
|
34
|
+
Data: str,
|
|
35
|
+
}
|
|
36
|
+
var reply string
|
|
37
|
+
err := vmClient.Call(vm.ReceiveMessageMethod, msg, &reply)
|
|
38
|
+
return reply, err
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return vmClientCodec, sendToNode
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type Fixture struct {
|
|
45
|
+
SendToNode Sender
|
|
46
|
+
SendToGo func (port int, msgStr string) string
|
|
47
|
+
ReplyToGo func (replyPort int, isError bool, respStr string) int
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func NewFixture(t *testing.T) (*Fixture) {
|
|
51
|
+
nodePort := 42
|
|
52
|
+
|
|
53
|
+
f := &Fixture{}
|
|
54
|
+
|
|
55
|
+
sendFunc := func(port int, reply int, str string) {
|
|
56
|
+
switch str {
|
|
57
|
+
case "ping":
|
|
58
|
+
time.AfterFunc(100*time.Millisecond, func() {
|
|
59
|
+
fmt.Printf("sendFunc: port=%d, reply=%d, str=%s\n", port, reply, str)
|
|
60
|
+
if reply != 0 {
|
|
61
|
+
f.ReplyToGo(reply, false, "pong")
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
case "wait":
|
|
65
|
+
time.AfterFunc(300*time.Millisecond, func() {
|
|
66
|
+
fmt.Printf("sendFunc: port=%d, reply=%d, str=%s\n", port, reply, str)
|
|
67
|
+
if reply != 0 {
|
|
68
|
+
f.ReplyToGo(reply, false, "done-waiting")
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
default:
|
|
72
|
+
t.Errorf("Unexpected message %s", str)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
vmClientCodec, sendToNode := ConnectVMClientCodec(
|
|
77
|
+
context.Background(),
|
|
78
|
+
int(nodePort),
|
|
79
|
+
sendFunc,
|
|
80
|
+
)
|
|
81
|
+
agdServer := vm.NewAgdServer()
|
|
82
|
+
|
|
83
|
+
f.SendToNode = sendToNode
|
|
84
|
+
f.SendToGo = func (port int, msgStr string) string {
|
|
85
|
+
fmt.Println("Send to Go", msgStr)
|
|
86
|
+
var respStr string
|
|
87
|
+
message := &vm.Message{
|
|
88
|
+
Port: int(port),
|
|
89
|
+
NeedsReply: true,
|
|
90
|
+
Data: msgStr,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
err := agdServer.ReceiveMessage(message, &respStr)
|
|
94
|
+
if err == nil {
|
|
95
|
+
return respStr
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// fmt.Fprintln(os.Stderr, "Cannot receive from controller", err)
|
|
99
|
+
errResp := errorWrapper{
|
|
100
|
+
Error: err.Error(),
|
|
101
|
+
}
|
|
102
|
+
respBytes, err := json.Marshal(&errResp)
|
|
103
|
+
if err != nil {
|
|
104
|
+
panic(err)
|
|
105
|
+
}
|
|
106
|
+
return string(respBytes)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
f.ReplyToGo = func (replyPort int, isError bool, respStr string) int {
|
|
110
|
+
if err := vmClientCodec.Receive(replyPort, isError, respStr); err != nil {
|
|
111
|
+
return 1
|
|
112
|
+
}
|
|
113
|
+
return 0
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return f
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
func TestClient_oneOutbound(t *testing.T) {
|
|
120
|
+
f := NewFixture(t)
|
|
121
|
+
|
|
122
|
+
// Try one blocking call.
|
|
123
|
+
ret, err := f.SendToNode(context.Background(), true, "ping")
|
|
124
|
+
if err != nil {
|
|
125
|
+
t.Error(err)
|
|
126
|
+
}
|
|
127
|
+
if ret != "pong" {
|
|
128
|
+
t.Errorf("ping want pong, got %s", ret)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
func TestClient_ShortBg(t *testing.T) {
|
|
133
|
+
|
|
134
|
+
f := NewFixture(t)
|
|
135
|
+
|
|
136
|
+
// Try a short background call with a long overlapping call.
|
|
137
|
+
done := make(chan struct{})
|
|
138
|
+
go func() {
|
|
139
|
+
defer close(done)
|
|
140
|
+
ret, err := f.SendToNode(context.Background(), true, "ping")
|
|
141
|
+
if err != nil {
|
|
142
|
+
t.Error(err)
|
|
143
|
+
}
|
|
144
|
+
if ret != "pong" {
|
|
145
|
+
t.Errorf("ping want pong, got %s", ret)
|
|
146
|
+
}
|
|
147
|
+
}()
|
|
148
|
+
|
|
149
|
+
ret, err := f.SendToNode(context.Background(), true, "wait")
|
|
150
|
+
if err != nil {
|
|
151
|
+
t.Error(err)
|
|
152
|
+
}
|
|
153
|
+
if ret != "done-waiting" {
|
|
154
|
+
t.Errorf("wait want done-waiting, got %s", ret)
|
|
155
|
+
}
|
|
156
|
+
<-done
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
func TestClient_LongBg(t *testing.T) {
|
|
160
|
+
|
|
161
|
+
f := NewFixture(t)
|
|
162
|
+
|
|
163
|
+
// Try a long background call with a short overlapping call.
|
|
164
|
+
done := make(chan struct{})
|
|
165
|
+
go func() {
|
|
166
|
+
defer close(done)
|
|
167
|
+
ret, err := f.SendToNode(context.Background(), true, "wait")
|
|
168
|
+
if err != nil {
|
|
169
|
+
t.Error(err)
|
|
170
|
+
}
|
|
171
|
+
if ret != "done-waiting" {
|
|
172
|
+
t.Errorf("ping want done-waiting, got %s", ret)
|
|
173
|
+
}
|
|
174
|
+
}()
|
|
175
|
+
|
|
176
|
+
ret, err := f.SendToNode(context.Background(), true, "ping")
|
|
177
|
+
if err != nil {
|
|
178
|
+
t.Error(err)
|
|
179
|
+
}
|
|
180
|
+
if ret != "pong" {
|
|
181
|
+
t.Errorf("wait want pong, got %s", ret)
|
|
182
|
+
}
|
|
183
|
+
<-done
|
|
184
|
+
}
|
package/vm/controller.go
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
package vm
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"
|
|
4
|
+
"context"
|
|
5
5
|
|
|
6
6
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
-
type ControllerContext struct {
|
|
10
|
-
Context sdk.Context
|
|
11
|
-
IBCChannelHandlerPort int
|
|
12
|
-
}
|
|
13
|
-
|
|
14
9
|
type ControllerAdmissionMsg interface {
|
|
15
10
|
sdk.Msg
|
|
16
11
|
CheckAdmissibility(sdk.Context, interface{}) error
|
|
@@ -31,10 +26,13 @@ type Jsonable interface{}
|
|
|
31
26
|
// ActionPusher enqueues data for later consumption by the controller.
|
|
32
27
|
type ActionPusher func(ctx sdk.Context, action Jsonable) error
|
|
33
28
|
|
|
34
|
-
var
|
|
29
|
+
var wrappedEmptySDKContext = sdk.WrapSDKContext(
|
|
30
|
+
sdk.Context{}.WithContext(context.Background()),
|
|
31
|
+
)
|
|
32
|
+
var controllerContext context.Context = wrappedEmptySDKContext
|
|
35
33
|
|
|
36
34
|
type PortHandler interface {
|
|
37
|
-
Receive(
|
|
35
|
+
Receive(context.Context, string) (string, error)
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
var portToHandler = make(map[int]PortHandler)
|
|
@@ -45,9 +43,9 @@ var lastPort = 0
|
|
|
45
43
|
func SetControllerContext(ctx sdk.Context) func() {
|
|
46
44
|
// We are only called by the controller, so we assume that it is billing its
|
|
47
45
|
// own meter usage.
|
|
48
|
-
controllerContext
|
|
46
|
+
controllerContext = sdk.WrapSDKContext(ctx.WithGasMeter(sdk.NewInfiniteGasMeter()))
|
|
49
47
|
return func() {
|
|
50
|
-
controllerContext
|
|
48
|
+
controllerContext = wrappedEmptySDKContext
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
|
|
@@ -69,11 +67,3 @@ func UnregisterPortHandler(portNum int) error {
|
|
|
69
67
|
delete(nameToPort, name)
|
|
70
68
|
return nil
|
|
71
69
|
}
|
|
72
|
-
|
|
73
|
-
func ReceiveFromController(portNum int, msg string) (string, error) {
|
|
74
|
-
handler := portToHandler[portNum]
|
|
75
|
-
if handler == nil {
|
|
76
|
-
return "", fmt.Errorf("unregistered port %d", portNum)
|
|
77
|
-
}
|
|
78
|
-
return handler.Receive(&controllerContext, msg)
|
|
79
|
-
}
|
|
@@ -0,0 +1,160 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
}
|
package/x/lien/lien.go
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
package lien
|
|
6
6
|
|
|
7
7
|
import (
|
|
8
|
+
"context"
|
|
8
9
|
"encoding/json"
|
|
9
10
|
"fmt"
|
|
10
11
|
"math"
|
|
@@ -70,7 +71,8 @@ const (
|
|
|
70
71
|
// Receives and processes a bridge message, returning the
|
|
71
72
|
// JSON-encoded response or error.
|
|
72
73
|
// See spec/02_messages.md for the messages and responses.
|
|
73
|
-
func (ch portHandler) Receive(
|
|
74
|
+
func (ch portHandler) Receive(cctx context.Context, str string) (string, error) {
|
|
75
|
+
ctx := sdk.UnwrapSDKContext(cctx)
|
|
74
76
|
var msg portMessage
|
|
75
77
|
err := json.Unmarshal([]byte(str), &msg)
|
|
76
78
|
if err != nil {
|
|
@@ -78,13 +80,13 @@ func (ch portHandler) Receive(ctx *vm.ControllerContext, str string) (string, er
|
|
|
78
80
|
}
|
|
79
81
|
switch msg.Type {
|
|
80
82
|
case LIEN_GET_ACCOUNT_STATE:
|
|
81
|
-
return ch.handleGetAccountState(ctx
|
|
83
|
+
return ch.handleGetAccountState(ctx, msg)
|
|
82
84
|
|
|
83
85
|
case LIEN_GET_STAKING:
|
|
84
|
-
return ch.handleGetStaking(ctx
|
|
86
|
+
return ch.handleGetStaking(ctx, msg)
|
|
85
87
|
|
|
86
88
|
case LIEN_CHANGE_LIENED:
|
|
87
|
-
return ch.handleChangeLiened(ctx
|
|
89
|
+
return ch.handleChangeLiened(ctx, msg)
|
|
88
90
|
}
|
|
89
91
|
return "", fmt.Errorf("unrecognized type %s", msg.Type)
|
|
90
92
|
}
|