Commit 32bed50b authored by Taylor Gerring's avatar Taylor Gerring

begin conversion to rpc over http

Per specification at
https://github.com/ethereum/wiki/wiki/Generic-JSON-RPC
parent c9985bf5
......@@ -3,6 +3,7 @@ package rpc
import (
"encoding/json"
"io"
"net/http"
)
type jsonWrapper struct{}
......@@ -11,10 +12,76 @@ func (self jsonWrapper) Send(writer io.Writer, v interface{}) (n int, err error)
var payload []byte
payload, err = json.Marshal(v)
if err != nil {
jsonlogger.Fatalln("Error marshalling JSON", err)
return 0, err
}
jsonlogger.Infof("Sending payload: %s", payload)
return writer.Write(payload)
}
func (self jsonWrapper) ParseRequestBody(req *http.Request) (RpcRequest, error) {
var reqParsed RpcRequest
// Convert JSON to native types
d := json.NewDecoder(req.Body)
// d.UseNumber()
defer req.Body.Close()
err := d.Decode(&reqParsed)
if err != nil {
jsonlogger.Errorln("Error decoding JSON: ", err)
return reqParsed, err
}
jsonlogger.DebugDetailf("Parsed request: %s", reqParsed)
return reqParsed, nil
}
func (self jsonWrapper) GetRequestReply(xeth *EthereumApi, req *RpcRequest, reply *interface{}) error {
// call function for request method
jsonlogger.DebugDetailf("%T %s", req.Params, req.Params)
switch req.Method {
case "eth_coinbase":
return xeth.GetCoinbase(reply)
case "eth_listening":
return xeth.GetIsListening(reply)
case "eth_mining":
return xeth.GetIsMining(reply)
case "eth_peerCount":
return xeth.GetPeerCount(reply)
case "eth_countAt":
args, err := req.ToGetTxCountArgs()
if err != nil {
return err
}
return xeth.GetTxCountAt(args, reply)
// case "eth_codeAt":
// return nil
case "eth_balanceAt":
args, err := req.ToGetBalanceArgs()
if err != nil {
return err
}
return xeth.GetBalanceAt(args, reply)
case "eth_stateAt":
args, err := req.ToGetStorageArgs()
if err != nil {
return err
}
return xeth.GetStorageAt(args, reply)
case "eth_blockByNumber", "eth_blockByHash":
args, err := req.ToGetBlockArgs()
if err != nil {
return err
}
return xeth.GetBlock(args, reply)
default:
return NewErrorResponse(ErrorNotImplemented)
}
jsonlogger.DebugDetailf("Reply: %T %s", reply, reply)
return nil
}
var JSON jsonWrapper
package rpc
import "github.com/ethereum/go-ethereum/ethutil"
import (
"bytes"
"encoding/json"
"errors"
type Message struct {
Call string `json:"call"`
Args []interface{} `json:"args"`
Id int `json:"_id"`
Data interface{} `json:"data"`
// "github.com/ethereum/go-ethereum/ethutil"
)
const (
ErrorArguments = "Error: Insufficient arguments"
ErrorNotImplemented = "Error: Method not implemented"
ErrorUnknown = "Error: Unknown error"
ErrorParseRequest = "Error: Could not parse request"
ErrorDecodeArgs = "Error: Could not decode arguments"
)
// type JsonResponse interface {
// }
type ErrorResponse struct {
Error bool `json:"error"`
ErrorText string `json:"errorText"`
}
// type SuccessRes struct {
// Error bool `json:"error"`
// Result JsonResponse `json:"result"`
// }
// type Message struct {
// Call string `json:"call"`
// Args []interface{} `json:"args"`
// Id int `json:"_id"`
// Data interface{} `json:"data"`
// }
// func (self *Message) Arguments() *ethutil.Value {
// return ethutil.NewValue(self.Args)
// }
type RpcSuccessResponse struct {
ID int `json:"id"`
JsonRpc string `json:"jsonrpc"`
Error bool `json:"error"`
Result interface{} `json:"result"`
}
func (self *Message) Arguments() *ethutil.Value {
return ethutil.NewValue(self.Args)
type RpcErrorResponse struct {
ID int `json:"id"`
JsonRpc string `json:"jsonrpc"`
Error bool `json:"error"`
ErrorText string `json:"errortext"`
}
type RpcRequest struct {
JsonRpc string `json:"jsonrpc"`
ID int `json:"id"`
Method string `json:"method"`
Params []json.RawMessage `json:"params"`
}
func (req *RpcRequest) ToGetBlockArgs() (*GetBlockArgs, error) {
if len(req.Params) < 1 {
return nil, NewErrorResponse(ErrorArguments)
}
args := new(GetBlockArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, NewErrorResponse(ErrorDecodeArgs)
}
jsonlogger.DebugDetailf("%T %v", args, args)
return args, nil
}
func (req *RpcRequest) ToNewTxArgs() (*NewTxArgs, error) {
if len(req.Params) < 7 {
return nil, NewErrorResponse(ErrorArguments)
}
args := new(NewTxArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, NewErrorResponse(ErrorDecodeArgs)
}
jsonlogger.DebugDetailf("%T %v", args, args)
return args, nil
}
func (req *RpcRequest) ToPushTxArgs() (*PushTxArgs, error) {
if len(req.Params) < 1 {
return nil, NewErrorResponse(ErrorArguments)
}
args := new(PushTxArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, NewErrorResponse(ErrorDecodeArgs)
}
jsonlogger.DebugDetailf("%T %v", args, args)
return args, nil
}
func (req *RpcRequest) ToGetStorageArgs() (*GetStorageArgs, error) {
if len(req.Params) < 2 {
return nil, NewErrorResponse(ErrorArguments)
}
args := new(GetStorageArgs)
// TODO need to pass both arguments
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, NewErrorResponse(ErrorDecodeArgs)
}
jsonlogger.DebugDetailf("%T %v", args, args)
return args, nil
}
func (req *RpcRequest) ToGetTxCountArgs() (*GetTxCountArgs, error) {
if len(req.Params) < 1 {
return nil, NewErrorResponse(ErrorArguments)
}
args := new(GetTxCountArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, NewErrorResponse(ErrorDecodeArgs)
}
jsonlogger.DebugDetailf("%T %v", args, args)
return args, nil
}
func (req *RpcRequest) ToGetBalanceArgs() (*GetBalanceArgs, error) {
if len(req.Params) < 1 {
return nil, NewErrorResponse(ErrorArguments)
}
args := new(GetBalanceArgs)
r := bytes.NewReader(req.Params[0])
err := json.NewDecoder(r).Decode(args)
if err != nil {
return nil, NewErrorResponse(ErrorDecodeArgs)
}
jsonlogger.DebugDetailf("%T %v", args, args)
return args, nil
}
// func NewSuccessRes(object JsonResponse) string {
// e := SuccessRes{Error: false, Result: object}
// res, err := json.Marshal(e)
// if err != nil {
// // This should never happen
// panic("Creating json error response failed, help")
// }
// success := string(res)
// return success
// // return res
// }
func NewErrorResponse(msg string) error {
return errors.New(msg)
}
This diff is collapsed.
......@@ -3,8 +3,7 @@ package rpc
import (
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"net/http"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/xeth"
......@@ -38,17 +37,14 @@ func (s *JsonRpcServer) Stop() {
func (s *JsonRpcServer) Start() {
jsonlogger.Infoln("Starting JSON-RPC server")
go s.exitHandler()
rpc.Register(&EthereumApi{pipe: s.pipe})
rpc.HandleHTTP()
for {
conn, err := s.listener.Accept()
if err != nil {
jsonlogger.Infoln("Error starting JSON-RPC:", err)
break
}
jsonlogger.Debugln("Incoming request.")
go jsonrpc.ServeConn(conn)
h := apiHandler(&EthereumApi{pipe: s.pipe})
http.Handle("/", h)
err := http.Serve(s.listener, nil)
// TODO Complains on shutdown due to listner already being closed
if err != nil {
jsonlogger.Errorln("Error on JSON-RPC interface:", err)
}
}
......@@ -65,3 +61,28 @@ func NewJsonRpcServer(pipe *xeth.JSXEth, port int) (*JsonRpcServer, error) {
pipe: pipe,
}, nil
}
func apiHandler(xeth *EthereumApi) http.Handler {
fn := func(w http.ResponseWriter, req *http.Request) {
jsonlogger.Debugln("Handling request")
reqParsed, reqerr := JSON.ParseRequestBody(req)
if reqerr != nil {
JSON.Send(w, &RpcErrorResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: true, ErrorText: ErrorParseRequest})
return
}
var response interface{}
reserr := JSON.GetRequestReply(xeth, &reqParsed, &response)
if reserr != nil {
jsonlogger.Errorln(reserr)
JSON.Send(w, &RpcErrorResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: true, ErrorText: reserr.Error()})
return
}
jsonlogger.Debugf("Generated response: %T %s", response, response)
JSON.Send(w, &RpcSuccessResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: false, Result: response})
}
return http.HandlerFunc(fn)
}
package rpc
/*
func pack(id int, v ...interface{}) Message {
return Message{Data: v, Id: id}
}
func WriteOn(msg *Message, writer io.Writer) {
//msg := &Message{Seed: seed, Data: data}
switch msg.Call {
case "compile":
data := ethutil.NewValue(msg.Args)
bcode, err := ethutil.Compile(data.Get(0).Str(), false)
if err != nil {
JSON.Send(writer, pack(msg.Id, err.Error()))
}
code := ethutil.Bytes2Hex(bcode)
JSON.Send(writer, pack(msg.Id, code, nil))
case "block":
args := msg.Arguments()
block := pipe.BlockByNumber(int32(args.Get(0).Uint()))
JSON.Send(writer, pack(msg.Id, block))
case "transact":
if mp, ok := msg.Args[0].(map[string]interface{}); ok {
object := mapToTxParams(mp)
JSON.Send(
writer,
pack(msg.Id, args(pipe.Transact(object["from"], object["to"], object["value"], object["gas"], object["gasPrice"], object["data"]))),
)
}
case "coinbase":
JSON.Send(writer, pack(msg.Id, pipe.CoinBase(), msg.Seed))
case "listening":
JSON.Send(writer, pack(msg.Id, pipe.IsListening()))
case "mining":
JSON.Send(writer, pack(msg.Id, pipe.IsMining()))
case "peerCoint":
JSON.Send(writer, pack(msg.Id, pipe.PeerCount()))
case "countAt":
args := msg.Arguments()
JSON.Send(writer, pack(msg.Id, pipe.TxCountAt(args.Get(0).Str())))
case "codeAt":
args := msg.Arguments()
JSON.Send(writer, pack(msg.Id, len(pipe.CodeAt(args.Get(0).Str()))))
case "stateAt":
args := msg.Arguments()
JSON.Send(writer, pack(msg.Id, pipe.StorageAt(args.Get(0).Str(), args.Get(1).Str())))
case "balanceAt":
args := msg.Arguments()
JSON.Send(writer, pack(msg.Id, pipe.BalanceAt(args.Get(0).Str())))
case "newFilter":
case "newFilterString":
case "messages":
// TODO
}
}
*/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment