aboutsummaryrefslogtreecommitdiffstats
path: root/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'rpc')
-rw-r--r--rpc/api.go8
-rw-r--r--rpc/args.go91
-rw-r--r--rpc/http.go25
-rw-r--r--rpc/responses.go6
-rw-r--r--rpc/types.go99
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)
+ }
+}