forked from filecoin-project/lotus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add RPC client (filecoin-project#103)
* add json rpc client * support onchain reconfiguration * use file membership by default * add compatibility test
- Loading branch information
Showing
11 changed files
with
346 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package ipcagent maintains IPC Agent client. | ||
package ipcagent |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package rpc | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"net/http" | ||
|
||
rpc "github.com/gorilla/rpc/v2/json2" | ||
) | ||
|
||
type JSONRPCRequestSender interface { | ||
SendRequest(method string, params interface{}, reply interface{}) error | ||
} | ||
|
||
var _ JSONRPCRequestSender = &JSONRPCClient{} | ||
|
||
type JSONRPCClient struct { | ||
ctx context.Context | ||
token string | ||
url string | ||
} | ||
|
||
func NewJSONRPCClient(url, token string) *JSONRPCClient { | ||
return &JSONRPCClient{ | ||
ctx: context.Background(), | ||
token: token, | ||
url: url, | ||
} | ||
} | ||
|
||
// NewInsecureJSONRPCClient creates a JSON RPC client with empty credentials. | ||
func NewInsecureJSONRPCClient(url string) *JSONRPCClient { | ||
return &JSONRPCClient{ | ||
ctx: context.Background(), | ||
token: "", | ||
url: url, | ||
} | ||
} | ||
|
||
func NewJSONRPCClientWithContext(ctx context.Context, url, token string) *JSONRPCClient { | ||
return &JSONRPCClient{ | ||
ctx: ctx, | ||
token: token, | ||
url: url, | ||
} | ||
} | ||
|
||
func (c *JSONRPCClient) SendRequest(method string, params interface{}, reply interface{}) error { | ||
paramBytes, err := rpc.EncodeClientRequest(method, params) | ||
if err != nil { | ||
return fmt.Errorf("failed to encode client params: %w", err) | ||
} | ||
|
||
req, err := http.NewRequestWithContext( | ||
c.ctx, | ||
"POST", | ||
c.url, | ||
bytes.NewBuffer(paramBytes), | ||
) | ||
if err != nil { | ||
return fmt.Errorf("failed to create request: %w", err) | ||
} | ||
req.Header.Set("Content-Type", "application/json") | ||
if c.token != "" { | ||
req.Header.Add("Authorization", "Bearer "+c.token) | ||
} | ||
|
||
resp, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
return fmt.Errorf("failed to issue request: %w", err) | ||
} | ||
|
||
// Return an error for any nonsuccessful status code | ||
if resp.StatusCode < 200 || resp.StatusCode > 299 { | ||
// Drop any error during close to report the original error | ||
_ = resp.Body.Close() | ||
return fmt.Errorf("received status code: %d", resp.StatusCode) | ||
} | ||
|
||
if err := rpc.DecodeClientResponse(resp.Body, reply); err != nil { | ||
// Drop any error during close to report the original error | ||
_ = resp.Body.Close() | ||
return fmt.Errorf("failed to decode client response: %w", err) | ||
} | ||
return resp.Body.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package rpc | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
|
||
"github.com/gorilla/rpc/v2" | ||
"github.com/gorilla/rpc/v2/json2" | ||
"github.com/stretchr/testify/require" | ||
|
||
addr "github.com/filecoin-project/go-address" | ||
) | ||
|
||
type TestService struct{} | ||
|
||
type TestServiceRequest struct { | ||
A int | ||
B int | ||
} | ||
|
||
type TestServiceResponse struct { | ||
O int | ||
} | ||
|
||
func (t *TestService) Multiply(_ *http.Request, req *TestServiceRequest, res *TestServiceResponse) error { | ||
res.O = req.A * req.B | ||
return nil | ||
} | ||
|
||
func TestClient(t *testing.T) { | ||
s := rpc.NewServer() | ||
s.RegisterCodec(json2.NewCodec(), "application/json") | ||
err := s.RegisterService(new(TestService), "") | ||
require.NoError(t, err) | ||
require.Equal(t, true, s.HasMethod("TestService.Multiply")) | ||
|
||
token := "token123" | ||
|
||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
require.Equal(t, "POST", r.Method) | ||
require.Equal(t, "application/json", r.Header.Get("Content-Type")) | ||
require.Equal(t, "Bearer "+token, r.Header.Get("Authorization")) | ||
|
||
s.ServeHTTP(w, r) | ||
})) | ||
defer srv.Close() | ||
|
||
c := NewJSONRPCClient(srv.URL, token) | ||
|
||
var resp *TestServiceResponse | ||
err = c.SendRequest("TestService.Multiply", &TestServiceRequest{A: 1, B: 4}, &resp) | ||
require.NoError(t, err) | ||
require.Equal(t, 4, resp.O) | ||
} | ||
|
||
type Validator struct { | ||
Addr addr.Address `json:"addr"` | ||
NetAddr string `json:"net_addr"` | ||
W int64 `json:"weight"` | ||
} | ||
|
||
type Set struct { | ||
ConfigurationNumber uint64 `json:"config_number"` | ||
Validators []Validator `json:"validators"` | ||
} | ||
|
||
type confServiceResponse struct { | ||
ValidatorSet Set `json:"validator_set"` | ||
} | ||
|
||
func TestClientCompatibleWithIPCAgent(t *testing.T) { | ||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
require.Equal(t, "POST", r.Method) | ||
require.Equal(t, "application/json", r.Header.Get("Content-Type")) | ||
|
||
obj := make(map[string]interface{}) | ||
|
||
d := json.NewDecoder(r.Body) | ||
err := d.Decode(&obj) | ||
require.NoError(t, err) | ||
|
||
require.Equal(t, "2.0", obj["jsonrpc"]) | ||
require.NotEqual(t, "", obj["id"]) | ||
require.NotEqual(t, "", obj["params"]) | ||
require.Equal(t, "ipc_queryValidatorSet", obj["method"]) | ||
|
||
var result = ` | ||
{ | ||
"jsonrpc": "2.0", | ||
"result": { | ||
"validator_set": { | ||
"config_number": 22, | ||
"validators": [{ | ||
"addr": "f1cp4q4lqsdhob23ysywffg2tvbmar5cshia4rweq", | ||
"net_addr": "/ip4/127.0.0.1/tcp/38443/p2p/12D3KooWM4Z6tymWBUC9LQ7NNJ2RtzoakV1vDSyzehzC17Dpo367", | ||
"weight": 0 | ||
}, | ||
{ | ||
"addr": "f1akaouty2buxxwb46l27pzrhl3te2lw5jem67xuy", | ||
"net_addr": "/ip4/127.0.0.1/tcp/40315/p2p/12D3KooWD9DHVsaPvBN5H16aWZ9KDChyrDSKVCnZegsJguuwd76E", | ||
"weight": 0 | ||
} | ||
] | ||
} | ||
}, | ||
"id": "5577006791947779410" | ||
} | ||
` | ||
_, err = fmt.Fprint(w, result) | ||
require.NoError(t, err) | ||
})) | ||
defer srv.Close() | ||
|
||
req := struct { | ||
subnet string | ||
tipSet string | ||
}{ | ||
subnet: "/root/test", | ||
tipSet: "QmPK1s3pNYLi9ERiq3BDxKa3XosgWwFRQUydHUtz4YgpqB", | ||
} | ||
|
||
var resp *confServiceResponse | ||
c := NewJSONRPCClient(srv.URL, "") | ||
err := c.SendRequest("ipc_queryValidatorSet", req, &resp) | ||
require.NoError(t, err) | ||
|
||
require.Equal(t, uint64(22), resp.ValidatorSet.ConfigurationNumber) | ||
require.Equal(t, 2, len(resp.ValidatorSet.Validators)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.