diff options
Diffstat (limited to 'rpc')
-rw-r--r-- | rpc/api.go | 8 | ||||
-rw-r--r-- | rpc/args.go | 91 | ||||
-rw-r--r-- | rpc/http.go | 25 | ||||
-rw-r--r-- | rpc/responses.go | 6 | ||||
-rw-r--r-- | rpc/types.go | 99 |
5 files changed, 177 insertions, 52 deletions
diff --git a/rpc/api.go b/rpc/api.go index bf5066f9a..66283752b 100644 --- a/rpc/api.go +++ b/rpc/api.go @@ -2,7 +2,7 @@ package rpc import ( "encoding/json" - // "fmt" + "fmt" "math/big" "sync" @@ -167,6 +167,12 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err return err } + // call ConfirmTransaction first + tx, _ := json.Marshal(req) + if !api.xeth().ConfirmTransaction(string(tx)) { + return fmt.Errorf("Transaction not confirmed") + } + v, err := api.xeth().Transact(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) if err != nil { return err diff --git a/rpc/args.go b/rpc/args.go index 4b3840285..d31773ff7 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -53,22 +53,23 @@ func blockHeight(raw interface{}, number *int64) error { return nil } -func numString(raw interface{}, number *int64) error { +func numString(raw interface{}) (*big.Int, error) { + var number *big.Int // Parse as integer num, ok := raw.(float64) if ok { - *number = int64(num) - return nil + number = big.NewInt(int64(num)) + return number, nil } // Parse as string/hexstring str, ok := raw.(string) - if !ok { - return NewInvalidTypeError("", "not a number or string") + if ok { + number = common.String2Big(str) + return number, nil } - *number = common.String2Big(str).Int64() - return nil + return nil, NewInvalidTypeError("", "not a number or string") } // func toNumber(v interface{}) (int64, error) { @@ -202,33 +203,36 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { args.To = ext.To args.Data = ext.Data - var num int64 + var num *big.Int if ext.Value == nil { - num = 0 + num = big.NewInt(0) } else { - if err := numString(ext.Value, &num); err != nil { + num, err = numString(ext.Value) + if err != nil { return err } } - args.Value = big.NewInt(num) + args.Value = num + num = nil if ext.Gas == nil { - num = 0 + num = big.NewInt(0) } else { - if err := numString(ext.Gas, &num); err != nil { + if num, err = numString(ext.Gas); err != nil { return err } } - args.Gas = big.NewInt(num) + args.Gas = num + num = nil if ext.GasPrice == nil { - num = 0 + num = big.NewInt(0) } else { - if err := numString(ext.GasPrice, &num); err != nil { + if num, err = numString(ext.GasPrice); err != nil { return err } } - args.GasPrice = big.NewInt(num) + args.GasPrice = num // Check for optional BlockNumber param if len(obj) > 1 { @@ -286,33 +290,33 @@ func (args *CallArgs) UnmarshalJSON(b []byte) (err error) { } args.To = ext.To - var num int64 + var num *big.Int if ext.Value == nil { - num = int64(0) + num = big.NewInt(0) } else { - if err := numString(ext.Value, &num); err != nil { + if num, err = numString(ext.Value); err != nil { return err } } - args.Value = big.NewInt(num) + args.Value = num if ext.Gas == nil { - num = int64(0) + num = big.NewInt(0) } else { - if err := numString(ext.Gas, &num); err != nil { + if num, err = numString(ext.Gas); err != nil { return err } } - args.Gas = big.NewInt(num) + args.Gas = num if ext.GasPrice == nil { - num = int64(0) + num = big.NewInt(0) } else { - if err := numString(ext.GasPrice, &num); err != nil { + if num, err = numString(ext.GasPrice); err != nil { return err } } - args.GasPrice = big.NewInt(num) + args.GasPrice = num args.Data = ext.Data @@ -655,6 +659,7 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { // return NewDecodeParamError(fmt.Sprintf("ToBlock %v", err)) var num int64 + var numBig *big.Int // if blank then latest if obj[0].FromBlock == nil { @@ -682,22 +687,22 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { args.Latest = num if obj[0].Limit == nil { - num = defaultLogLimit + numBig = big.NewInt(defaultLogLimit) } else { - if err := numString(obj[0].Limit, &num); err != nil { + if numBig, err = numString(obj[0].Limit); err != nil { return err } } - args.Max = int(num) + args.Max = int(numBig.Int64()) if obj[0].Offset == nil { - num = defaultLogOffset + numBig = big.NewInt(defaultLogOffset) } else { - if err := numString(obj[0].Offset, &num); err != nil { + if numBig, err = numString(obj[0].Offset); err != nil { return err } } - args.Skip = int(num) + args.Skip = int(numBig.Int64()) if obj[0].Address != nil { marg, ok := obj[0].Address.([]interface{}) @@ -739,10 +744,14 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) { for j, jv := range argarray { if v, ok := jv.(string); ok { topicdbl[i][j] = v + } else if jv == nil { + topicdbl[i][j] = "" } else { return NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", i, j), "is not a string") } } + } else if iv == nil { + topicdbl[i] = []string{""} } else { return NewInvalidTypeError(fmt.Sprintf("topic[%d]", i), "not a string or array") } @@ -890,16 +899,16 @@ func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) { args.From = obj[0].From args.Topics = obj[0].Topics - var num int64 - if err := numString(obj[0].Priority, &num); err != nil { + var num *big.Int + if num, err = numString(obj[0].Priority); err != nil { return err } - args.Priority = uint32(num) + args.Priority = uint32(num.Int64()) - if err := numString(obj[0].Ttl, &num); err != nil { + if num, err = numString(obj[0].Ttl); err != nil { return err } - args.Ttl = uint32(num) + args.Ttl = uint32(num.Int64()) return nil } @@ -969,11 +978,11 @@ func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { return NewInsufficientParamsError(len(obj), 1) } - var num int64 - if err := numString(obj[0], &num); err != nil { + var num *big.Int + if num, err = numString(obj[0]); err != nil { return err } - args.Id = int(num) + args.Id = int(num.Int64()) return nil } diff --git a/rpc/http.go b/rpc/http.go index 790442a28..f9c646908 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "io/ioutil" - "net" "net/http" "github.com/ethereum/go-ethereum/logger" @@ -15,6 +14,7 @@ import ( ) var rpclogger = logger.NewLogger("RPC") +var rpclistener *stoppableTCPListener const ( jsonrpcver = "2.0" @@ -22,11 +22,19 @@ const ( ) func Start(pipe *xeth.XEth, config RpcConfig) error { - l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort)) + if rpclistener != nil { + if fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort) != rpclistener.Addr().String() { + return fmt.Errorf("RPC service already running on %s ", rpclistener.Addr().String()) + } + return nil // RPC service already running on given host/port + } + + l, err := newStoppableTCPListener(fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort)) if err != nil { rpclogger.Errorf("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err) return err } + rpclistener = l var handler http.Handler if len(config.CorsDomain) > 0 { @@ -35,9 +43,9 @@ func Start(pipe *xeth.XEth, config RpcConfig) error { opts.AllowedOrigins = []string{config.CorsDomain} c := cors.New(opts) - handler = c.Handler(JSONRPC(pipe)) + handler = newStoppableHandler(c.Handler(JSONRPC(pipe)), l.stop) } else { - handler = JSONRPC(pipe) + handler = newStoppableHandler(JSONRPC(pipe), l.stop) } go http.Serve(l, handler) @@ -45,6 +53,15 @@ func Start(pipe *xeth.XEth, config RpcConfig) error { return nil } +func Stop() error { + if rpclistener != nil { + rpclistener.Stop() + rpclistener = nil + } + + return nil +} + // JSONRPC returns a handler that implements the Ethereum JSON-RPC API. func JSONRPC(pipe *xeth.XEth) http.Handler { api := NewEthereumApi(pipe) diff --git a/rpc/responses.go b/rpc/responses.go index 5d1be8f34..884b7e69b 100644 --- a/rpc/responses.go +++ b/rpc/responses.go @@ -24,7 +24,6 @@ type BlockRes struct { Size *hexnum `json:"size"` ExtraData *hexdata `json:"extraData"` GasLimit *hexnum `json:"gasLimit"` - MinGasPrice *hexnum `json:"minGasPrice"` GasUsed *hexnum `json:"gasUsed"` UnixTimestamp *hexnum `json:"timestamp"` Transactions []*TransactionRes `json:"transactions"` @@ -48,7 +47,6 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { Size *hexnum `json:"size"` ExtraData *hexdata `json:"extraData"` GasLimit *hexnum `json:"gasLimit"` - MinGasPrice *hexnum `json:"minGasPrice"` GasUsed *hexnum `json:"gasUsed"` UnixTimestamp *hexnum `json:"timestamp"` Transactions []*TransactionRes `json:"transactions"` @@ -69,7 +67,6 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { ext.Size = b.Size ext.ExtraData = b.ExtraData ext.GasLimit = b.GasLimit - ext.MinGasPrice = b.MinGasPrice ext.GasUsed = b.GasUsed ext.UnixTimestamp = b.UnixTimestamp ext.Transactions = b.Transactions @@ -94,7 +91,6 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { Size *hexnum `json:"size"` ExtraData *hexdata `json:"extraData"` GasLimit *hexnum `json:"gasLimit"` - MinGasPrice *hexnum `json:"minGasPrice"` GasUsed *hexnum `json:"gasUsed"` UnixTimestamp *hexnum `json:"timestamp"` Transactions []*hexdata `json:"transactions"` @@ -115,7 +111,6 @@ func (b *BlockRes) MarshalJSON() ([]byte, error) { ext.Size = b.Size ext.ExtraData = b.ExtraData ext.GasLimit = b.GasLimit - ext.MinGasPrice = b.MinGasPrice ext.GasUsed = b.GasUsed ext.UnixTimestamp = b.UnixTimestamp ext.Transactions = make([]*hexdata, len(b.Transactions)) @@ -151,7 +146,6 @@ func NewBlockRes(block *types.Block, fullTx bool) *BlockRes { res.Size = newHexNum(block.Size().Int64()) res.ExtraData = newHexData(block.Header().Extra) res.GasLimit = newHexNum(block.GasLimit()) - // res.MinGasPrice = res.GasUsed = newHexNum(block.GasUsed()) res.UnixTimestamp = newHexNum(block.Time()) diff --git a/rpc/types.go b/rpc/types.go index bc9a46ed5..1784759a4 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -23,6 +23,13 @@ import ( "math/big" "strings" + "errors" + "net" + "net/http" + "time" + + "io" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) @@ -257,3 +264,95 @@ type RpcErrorObject struct { Message string `json:"message"` // Data interface{} `json:"data"` } + +type listenerHasStoppedError struct { + msg string +} + +func (self listenerHasStoppedError) Error() string { + return self.msg +} + +var listenerStoppedError = listenerHasStoppedError{"Listener stopped"} + +// When https://github.com/golang/go/issues/4674 is fixed this could be replaced +type stoppableTCPListener struct { + *net.TCPListener + stop chan struct{} // closed when the listener must stop +} + +// Wraps the default handler and checks if the RPC service was stopped. In that case it returns an +// error indicating that the service was stopped. This will only happen for connections which are +// kept open (HTTP keep-alive) when the RPC service was shutdown. +func newStoppableHandler(h http.Handler, stop chan struct{}) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + select { + case <-stop: + w.Header().Set("Content-Type", "application/json") + jsonerr := &RpcErrorObject{-32603, "RPC service stopped"} + send(w, &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: nil, Error: jsonerr}) + default: + h.ServeHTTP(w, r) + } + }) +} + +// Stop the listener and all accepted and still active connections. +func (self *stoppableTCPListener) Stop() { + close(self.stop) +} + +func newStoppableTCPListener(addr string) (*stoppableTCPListener, error) { + wl, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + + if tcpl, ok := wl.(*net.TCPListener); ok { + stop := make(chan struct{}) + l := &stoppableTCPListener{tcpl, stop} + return l, nil + } + + return nil, errors.New("Unable to create TCP listener for RPC service") +} + +func (self *stoppableTCPListener) Accept() (net.Conn, error) { + for { + self.SetDeadline(time.Now().Add(time.Duration(1 * time.Second))) + c, err := self.TCPListener.AcceptTCP() + + select { + case <-self.stop: + if c != nil { // accept timeout + c.Close() + } + self.TCPListener.Close() + return nil, listenerStoppedError + default: + } + + if err != nil { + if netErr, ok := err.(net.Error); ok && netErr.Timeout() && netErr.Temporary() { + continue // regular timeout + } + } + + return &closableConnection{c, self.stop}, err + } +} + +type closableConnection struct { + *net.TCPConn + closed chan struct{} +} + +func (self *closableConnection) Read(b []byte) (n int, err error) { + select { + case <-self.closed: + self.TCPConn.Close() + return 0, io.EOF + default: + return self.TCPConn.Read(b) + } +} |