From ebdf339a614b9d03a0b0a0292d1ea24f854d6b3e Mon Sep 17 00:00:00 2001 From: Maran Date: Fri, 2 May 2014 13:35:25 +0200 Subject: [PATCH] Implemented RPC framework --- ethereum.go | 4 + etherpc/packages.go | 194 ++++++++++++++++++++++++++++++++++++++++++++ etherpc/server.go | 59 ++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 etherpc/packages.go create mode 100644 etherpc/server.go diff --git a/ethereum.go b/ethereum.go index c906a6954..df8e9ef7d 100644 --- a/ethereum.go +++ b/ethereum.go @@ -4,6 +4,7 @@ import ( "container/list" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/etherpc" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "io/ioutil" @@ -62,6 +63,8 @@ type Ethereum struct { MaxPeers int reactor *ethutil.ReactorEngine + + RpcServer *etherpc.JsonRpcServer } func New(caps Caps, usePnp bool) (*Ethereum, error) { @@ -338,6 +341,7 @@ func (s *Ethereum) Stop() { s.txPool.Stop() s.stateManager.Stop() + s.RpcServer.Stop() close(s.shutdownChan) } diff --git a/etherpc/packages.go b/etherpc/packages.go new file mode 100644 index 000000000..8bc78498f --- /dev/null +++ b/etherpc/packages.go @@ -0,0 +1,194 @@ +package etherpc + +import ( + "encoding/json" + "errors" + "math/big" +) + +type MainPackage struct{} + +type JsonArgs interface { + requirements() error +} + +type BlockResponse struct { + Name string + Id int +} +type GetBlockArgs struct { + BlockNumber int + Hash string +} + +type ErrorResponse struct { + Error bool `json:"error"` + ErrorText string `json:"errorText"` +} + +type JsonResponse interface { +} + +type SuccessRes struct { + Error bool `json:"error"` + Result JsonResponse `json:"result"` +} + +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 +} + +func NewErrorResponse(msg string) error { + e := ErrorResponse{Error: true, ErrorText: msg} + res, err := json.Marshal(e) + if err != nil { + // This should never happen + panic("Creating json error response failed, help") + } + newErr := errors.New(string(res)) + return newErr +} + +func (b *GetBlockArgs) requirements() error { + if b.BlockNumber == 0 && b.Hash == "" { + return NewErrorResponse("GetBlock requires either a block 'number' or a block 'hash' as argument") + } + return nil +} + +func (p *MainPackage) GetBlock(args *GetBlockArgs, reply *BlockResponse) error { + err := args.requirements() + if err != nil { + return err + } + // Do something + + return nil +} + +type NewTxArgs struct { + Sec string + Recipient string + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Init string + Body string +} +type TxResponse struct { + Hash string +} + +func (a *NewTxArgs) requirements() error { + if a.Recipient == "" { + return NewErrorResponse("Transact requires a 'recipient' address as argument") + } + if a.Value == nil { + return NewErrorResponse("Transact requires a 'value' as argument") + } + if a.Gas == nil { + return NewErrorResponse("Transact requires a 'gas' value as argument") + } + if a.GasPrice == nil { + return NewErrorResponse("Transact requires a 'gasprice' value as argument") + } + return nil +} + +func (a *NewTxArgs) requirementsContract() error { + if a.Value == nil { + return NewErrorResponse("Create requires a 'value' as argument") + } + if a.Gas == nil { + return NewErrorResponse("Create requires a 'gas' value as argument") + } + if a.GasPrice == nil { + return NewErrorResponse("Create requires a 'gasprice' value as argument") + } + if a.Init == "" { + return NewErrorResponse("Create requires a 'init' value as argument") + } + if a.Body == "" { + return NewErrorResponse("Create requires a 'body' value as argument") + } + return nil +} + +func (p *MainPackage) Transact(args *NewTxArgs, reply *TxResponse) error { + err := args.requirements() + if err != nil { + return err + } + return nil +} + +func (p *MainPackage) Create(args *NewTxArgs, reply *string) error { + err := args.requirementsContract() + if err != nil { + return err + } + return nil +} + +func (p *MainPackage) getKey(args interface{}, reply *string) error { + return nil +} + +type GetStorageArgs struct { + Address string + Key string +} + +func (a *GetStorageArgs) requirements() error { + if a.Address == "" { + return NewErrorResponse("GetStorageAt requires an 'address' value as argument") + } + if a.Key == "" { + return NewErrorResponse("GetStorageAt requires an 'key' value as argument") + } + return nil +} + +func (p *MainPackage) getStorageAt(args *GetStorageArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + return nil +} + +type GetBalanceArgs struct { + Address string +} + +func (a *GetBalanceArgs) requirements() error { + if a.Address == "" { + return NewErrorResponse("GetBalanceAt requires an 'address' value as argument") + } + return nil +} + +func (p *MainPackage) GetBalanceAt(args *GetBalanceArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + return nil +} + +type TestRes struct { + JsonResponse `json:"-"` + Answer int `json:"answer"` +} + +func (p *MainPackage) Test(args *GetBlockArgs, reply *string) error { + *reply = NewSuccessRes(TestRes{Answer: 15}) + return nil +} diff --git a/etherpc/server.go b/etherpc/server.go new file mode 100644 index 000000000..ba0c51006 --- /dev/null +++ b/etherpc/server.go @@ -0,0 +1,59 @@ +package etherpc + +import ( + "github.com/ethereum/eth-go/ethutil" + "net" + "net/rpc" + "net/rpc/jsonrpc" +) + +type JsonRpcServer struct { + quit chan bool + listener net.Listener +} + +func (s *JsonRpcServer) exitHandler() { +out: + for { + select { + case <-s.quit: + s.listener.Close() + break out + } + } + + ethutil.Config.Log.Infoln("[JSON] Shutdown JSON-RPC server") +} + +func (s *JsonRpcServer) Stop() { + close(s.quit) +} + +func (s *JsonRpcServer) Start() { + ethutil.Config.Log.Infoln("[JSON] Starting JSON-RPC server") + go s.exitHandler() + rpc.Register(new(MainPackage)) + rpc.HandleHTTP() + + for { + conn, err := s.listener.Accept() + if err != nil { + ethutil.Config.Log.Infoln("[JSON] Error starting JSON-RPC:", err) + continue + } + ethutil.Config.Log.Debugln("[JSON] Incoming request.") + go jsonrpc.ServeConn(conn) + } +} + +func NewJsonRpcServer() *JsonRpcServer { + l, err := net.Listen("tcp", ":30304") + if err != nil { + ethutil.Config.Log.Infoln("Error starting JSON-RPC") + } + + return &JsonRpcServer{ + listener: l, + quit: make(chan bool), + } +}