mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-27 11:41:39 +00:00
cleanup rpc/handlers
This commit is contained in:
@ -150,7 +150,7 @@ func (n *Node) DialSeed() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) StartRpc() {
|
func (n *Node) StartRPC() {
|
||||||
core.SetBlockStore(n.blockStore)
|
core.SetBlockStore(n.blockStore)
|
||||||
core.SetConsensusState(n.consensusState)
|
core.SetConsensusState(n.consensusState)
|
||||||
core.SetMempoolReactor(n.mempoolReactor)
|
core.SetMempoolReactor(n.mempoolReactor)
|
||||||
@ -185,7 +185,7 @@ func Daemon() {
|
|||||||
|
|
||||||
// Run the RPC server.
|
// Run the RPC server.
|
||||||
if config.App().GetString("RPC.HTTP.ListenAddr") != "" {
|
if config.App().GetString("RPC.HTTP.ListenAddr") != "" {
|
||||||
n.StartRpc()
|
n.StartRPC()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep forever and then...
|
// Sleep forever and then...
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO: support Call && GetStorage.
|
||||||
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -12,7 +16,6 @@ import (
|
|||||||
|
|
||||||
// cache all type information about each function up front
|
// cache all type information about each function up front
|
||||||
// (func, responseStruct, argNames)
|
// (func, responseStruct, argNames)
|
||||||
// XXX: response structs are allocated once and reused - will this cause an issue eg. if a field ever not overwritten?
|
|
||||||
var funcMap = map[string]*FuncWrapper{
|
var funcMap = map[string]*FuncWrapper{
|
||||||
"status": funcWrap(core.Status, []string{}),
|
"status": funcWrap(core.Status, []string{}),
|
||||||
"net_info": funcWrap(core.NetInfo, []string{}),
|
"net_info": funcWrap(core.NetInfo, []string{}),
|
||||||
@ -26,6 +29,18 @@ var funcMap = map[string]*FuncWrapper{
|
|||||||
"unsafe/sign_tx": funcWrap(core.SignTx, []string{"tx", "privAccounts"}),
|
"unsafe/sign_tx": funcWrap(core.SignTx, []string{"tx", "privAccounts"}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initHandlers() {
|
||||||
|
// HTTP endpoints
|
||||||
|
for funcName, funcInfo := range funcMap {
|
||||||
|
http.HandleFunc("/"+funcName, toHttpHandler(funcInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONRPC endpoints
|
||||||
|
http.HandleFunc("/", JSONRPCHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
|
||||||
// holds all type information for each function
|
// holds all type information for each function
|
||||||
type FuncWrapper struct {
|
type FuncWrapper struct {
|
||||||
f reflect.Value // function from "rpc/core"
|
f reflect.Value // function from "rpc/core"
|
||||||
@ -43,170 +58,6 @@ func funcWrap(f interface{}, args []string) *FuncWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert from a function name to the http handler
|
|
||||||
func toHandler(funcName string) func(http.ResponseWriter, *http.Request) {
|
|
||||||
funcInfo := funcMap[funcName]
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
values, err := queryToValues(funcInfo, r)
|
|
||||||
if err != nil {
|
|
||||||
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
returns := funcInfo.f.Call(values)
|
|
||||||
response, err := returnsToResponse(funcInfo, returns)
|
|
||||||
if err != nil {
|
|
||||||
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
WriteAPIResponse(w, API_OK, response, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert a (json) string to a given type
|
|
||||||
func jsonToArg(ty reflect.Type, arg string) (reflect.Value, error) {
|
|
||||||
var err error
|
|
||||||
v := reflect.New(ty)
|
|
||||||
binary.ReadJSON(v.Interface(), []byte(arg), &err)
|
|
||||||
if err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
v = v.Elem()
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func jsonObjectToArg(ty reflect.Type, object interface{}) (reflect.Value, error) {
|
|
||||||
var err error
|
|
||||||
v := reflect.New(ty)
|
|
||||||
binary.ReadJSONFromObject(v.Interface(), object, &err)
|
|
||||||
if err != nil {
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
v = v.Elem()
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// covert an http query to a list of properly typed values.
|
|
||||||
// to be properly decoded the arg must be a concrete type from tendermint (if its an interface).
|
|
||||||
func queryToValues(funcInfo *FuncWrapper, r *http.Request) ([]reflect.Value, error) {
|
|
||||||
argTypes := funcInfo.args
|
|
||||||
argNames := funcInfo.argNames
|
|
||||||
|
|
||||||
var err error
|
|
||||||
values := make([]reflect.Value, len(argNames))
|
|
||||||
for i, name := range argNames {
|
|
||||||
ty := argTypes[i]
|
|
||||||
arg := GetParam(r, name)
|
|
||||||
//fmt.Println("GetParam()", r, name, arg)
|
|
||||||
values[i], err = jsonToArg(ty, arg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return values, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// covert a list of interfaces to properly typed values
|
|
||||||
func paramsToValues(funcInfo *FuncWrapper, params []interface{}) ([]reflect.Value, error) {
|
|
||||||
values := make([]reflect.Value, len(params))
|
|
||||||
for i, p := range params {
|
|
||||||
ty := funcInfo.args[i]
|
|
||||||
v, err := jsonObjectToArg(ty, p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
values[i] = v
|
|
||||||
}
|
|
||||||
return values, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns is Response struct and error. If error is not nil, return it
|
|
||||||
func returnsToResponse(funcInfo *FuncWrapper, returns []reflect.Value) (interface{}, error) {
|
|
||||||
errV := returns[1]
|
|
||||||
if errV.Interface() != nil {
|
|
||||||
return nil, fmt.Errorf("%v", errV.Interface())
|
|
||||||
}
|
|
||||||
return returns[0].Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
// convert a list of values to a populated struct with the correct types
|
|
||||||
func returnsToResponse(funcInfo *FuncWrapper, returns []reflect.Value) (interface{}, error) {
|
|
||||||
returnTypes := funcInfo.returns
|
|
||||||
finalType := returnTypes[len(returnTypes)-1]
|
|
||||||
if finalType.Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
|
||||||
errV := returns[len(returnTypes)-1]
|
|
||||||
if errV.Interface() != nil {
|
|
||||||
return nil, fmt.Errorf("%v", errV.Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy the response struct (New returns a pointer so we have to Elem() twice)
|
|
||||||
v := reflect.New(funcInfo.response.Elem().Type()).Elem()
|
|
||||||
nFields := v.NumField()
|
|
||||||
for i := 0; i < nFields; i++ {
|
|
||||||
field := v.FieldByIndex([]int{i})
|
|
||||||
field.Set(returns[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.Interface(), nil
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// jsonrpc calls grab the given method's function info and runs reflect.Call
|
|
||||||
func JsonRpcHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
b, _ := ioutil.ReadAll(r.Body)
|
|
||||||
var jrpc JsonRpc
|
|
||||||
err := json.Unmarshal(b, &jrpc)
|
|
||||||
if err != nil {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
funcInfo := funcMap[jrpc.Method]
|
|
||||||
values, err := paramsToValues(funcInfo, jrpc.Params)
|
|
||||||
if err != nil {
|
|
||||||
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
returns := funcInfo.f.Call(values)
|
|
||||||
response, err := returnsToResponse(funcInfo, returns)
|
|
||||||
if err != nil {
|
|
||||||
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
WriteAPIResponse(w, API_OK, response, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func initHandlers() {
|
|
||||||
// HTTP endpoints
|
|
||||||
// toHandler runs once for each function and caches
|
|
||||||
// all reflection data
|
|
||||||
http.HandleFunc("/status", toHandler("status"))
|
|
||||||
http.HandleFunc("/net_info", toHandler("net_info"))
|
|
||||||
http.HandleFunc("/blockchain", toHandler("blockchain"))
|
|
||||||
http.HandleFunc("/get_block", toHandler("get_block"))
|
|
||||||
http.HandleFunc("/get_account", toHandler("get_account"))
|
|
||||||
http.HandleFunc("/list_validators", toHandler("list_validators"))
|
|
||||||
http.HandleFunc("/broadcast_tx", toHandler("broadcast_tx"))
|
|
||||||
http.HandleFunc("/list_accounts", toHandler("list_accounts"))
|
|
||||||
http.HandleFunc("/unsafe/gen_priv_account", toHandler("unsafe/gen_priv_account"))
|
|
||||||
http.HandleFunc("/unsafe/sign_tx", toHandler("unsafe/sign_tx"))
|
|
||||||
//http.HandleFunc("/call", CallHandler)
|
|
||||||
//http.HandleFunc("/get_storage", GetStorageHandler)
|
|
||||||
|
|
||||||
// JsonRPC endpoints
|
|
||||||
http.HandleFunc("/", JsonRpcHandler)
|
|
||||||
// unsafe JsonRPC endpoints
|
|
||||||
//http.HandleFunc("/unsafe", UnsafeJsonRpcHandler)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonRpc struct {
|
|
||||||
JsonRpc string `json:"jsonrpc"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
Params []interface{} `json:"params"`
|
|
||||||
Id int `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// this will panic if not passed a function
|
|
||||||
func funcArgTypes(f interface{}) []reflect.Type {
|
func funcArgTypes(f interface{}) []reflect.Type {
|
||||||
t := reflect.TypeOf(f)
|
t := reflect.TypeOf(f)
|
||||||
n := t.NumIn()
|
n := t.NumIn()
|
||||||
@ -226,3 +77,126 @@ func funcReturnTypes(f interface{}) []reflect.Type {
|
|||||||
}
|
}
|
||||||
return types
|
return types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// rpc.json
|
||||||
|
|
||||||
|
type JSONRPC struct {
|
||||||
|
JSONRPC string `json:"jsonrpc"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Params []interface{} `json:"params"`
|
||||||
|
Id int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// jsonrpc calls grab the given method's function info and runs reflect.Call
|
||||||
|
func JSONRPCHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
b, _ := ioutil.ReadAll(r.Body)
|
||||||
|
var jrpc JSONRPC
|
||||||
|
err := json.Unmarshal(b, &jrpc)
|
||||||
|
if err != nil {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
funcInfo := funcMap[jrpc.Method]
|
||||||
|
args, err := jsonParamsToArgs(funcInfo, jrpc.Params)
|
||||||
|
if err != nil {
|
||||||
|
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
returns := funcInfo.f.Call(args)
|
||||||
|
response, err := returnsToResponse(returns)
|
||||||
|
if err != nil {
|
||||||
|
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
WriteAPIResponse(w, API_OK, response, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// covert a list of interfaces to properly typed values
|
||||||
|
func jsonParamsToArgs(funcInfo *FuncWrapper, params []interface{}) ([]reflect.Value, error) {
|
||||||
|
values := make([]reflect.Value, len(params))
|
||||||
|
for i, p := range params {
|
||||||
|
ty := funcInfo.args[i]
|
||||||
|
v, err := _jsonObjectToArg(ty, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
values[i] = v
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _jsonObjectToArg(ty reflect.Type, object interface{}) (reflect.Value, error) {
|
||||||
|
var err error
|
||||||
|
v := reflect.New(ty)
|
||||||
|
binary.ReadJSONFromObject(v.Interface(), object, &err)
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// rpc.json
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// rpc.http
|
||||||
|
|
||||||
|
// convert from a function name to the http handler
|
||||||
|
func toHttpHandler(funcInfo *FuncWrapper) func(http.ResponseWriter, *http.Request) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
args, err := httpParamsToArgs(funcInfo, r)
|
||||||
|
if err != nil {
|
||||||
|
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
returns := funcInfo.f.Call(args)
|
||||||
|
response, err := returnsToResponse(returns)
|
||||||
|
if err != nil {
|
||||||
|
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
WriteAPIResponse(w, API_OK, response, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Covert an http query to a list of properly typed values.
|
||||||
|
// To be properly decoded the arg must be a concrete type from tendermint (if its an interface).
|
||||||
|
func httpParamsToArgs(funcInfo *FuncWrapper, r *http.Request) ([]reflect.Value, error) {
|
||||||
|
argTypes := funcInfo.args
|
||||||
|
argNames := funcInfo.argNames
|
||||||
|
|
||||||
|
var err error
|
||||||
|
values := make([]reflect.Value, len(argNames))
|
||||||
|
for i, name := range argNames {
|
||||||
|
ty := argTypes[i]
|
||||||
|
arg := GetParam(r, name)
|
||||||
|
values[i], err = _jsonStringToArg(ty, arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) {
|
||||||
|
var err error
|
||||||
|
v := reflect.New(ty)
|
||||||
|
binary.ReadJSON(v.Interface(), []byte(arg), &err)
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// rpc.http
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// returns is Response struct and error. If error is not nil, return it
|
||||||
|
func returnsToResponse(returns []reflect.Value) (interface{}, error) {
|
||||||
|
errV := returns[1]
|
||||||
|
if errV.Interface() != nil {
|
||||||
|
return nil, fmt.Errorf("%v", errV.Interface())
|
||||||
|
}
|
||||||
|
return returns[0].Interface(), nil
|
||||||
|
}
|
@ -18,8 +18,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestJSONStatus(t *testing.T) {
|
func TestJSONStatus(t *testing.T) {
|
||||||
s := rpc.JsonRpc{
|
s := rpc.JSONRPC{
|
||||||
JsonRpc: "2.0",
|
JSONRPC: "2.0",
|
||||||
Method: "status",
|
Method: "status",
|
||||||
Params: []interface{}{},
|
Params: []interface{}{},
|
||||||
Id: 0,
|
Id: 0,
|
||||||
@ -53,8 +53,8 @@ func TestJSONStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJSONGenPriv(t *testing.T) {
|
func TestJSONGenPriv(t *testing.T) {
|
||||||
s := rpc.JsonRpc{
|
s := rpc.JSONRPC{
|
||||||
JsonRpc: "2.0",
|
JSONRPC: "2.0",
|
||||||
Method: "unsafe/gen_priv_account",
|
Method: "unsafe/gen_priv_account",
|
||||||
Params: []interface{}{},
|
Params: []interface{}{},
|
||||||
Id: 0,
|
Id: 0,
|
||||||
|
@ -41,7 +41,7 @@ func newNode(ready chan struct{}) {
|
|||||||
node.Start()
|
node.Start()
|
||||||
|
|
||||||
// Run the RPC server.
|
// Run the RPC server.
|
||||||
node.StartRpc()
|
node.StartRPC()
|
||||||
ready <- struct{}{}
|
ready <- struct{}{}
|
||||||
|
|
||||||
// Sleep forever
|
// Sleep forever
|
||||||
@ -72,8 +72,8 @@ func getAccount(t *testing.T, typ string, addr []byte) *account.Account {
|
|||||||
var err error
|
var err error
|
||||||
switch typ {
|
switch typ {
|
||||||
case "JSONRPC":
|
case "JSONRPC":
|
||||||
s := rpc.JsonRpc{
|
s := rpc.JSONRPC{
|
||||||
JsonRpc: "2.0",
|
JSONRPC: "2.0",
|
||||||
Method: "get_account",
|
Method: "get_account",
|
||||||
Params: []interface{}{hex.EncodeToString(addr)},
|
Params: []interface{}{hex.EncodeToString(addr)},
|
||||||
Id: 0,
|
Id: 0,
|
||||||
|
Reference in New Issue
Block a user