diff options
Diffstat (limited to 'rpc')
-rw-r--r-- | rpc/args.go | 150 | ||||
-rw-r--r-- | rpc/http/server.go | 15 | ||||
-rw-r--r-- | rpc/message.go | 217 | ||||
-rw-r--r-- | rpc/packages.go | 313 | ||||
-rw-r--r-- | rpc/util.go (renamed from rpc/json.go) | 48 | ||||
-rw-r--r-- | rpc/ws/server.go | 36 |
6 files changed, 651 insertions, 128 deletions
diff --git a/rpc/args.go b/rpc/args.go index 8b01cc191..84b076d4a 100644 --- a/rpc/args.go +++ b/rpc/args.go @@ -1,8 +1,7 @@ package rpc -import ( - "encoding/json" -) +import "encoding/json" +import "github.com/ethereum/go-ethereum/core" type GetBlockArgs struct { BlockNumber int32 @@ -30,56 +29,12 @@ func (obj *GetBlockArgs) requirements() error { } type NewTxArgs struct { - Sec string `json:"sec"` - Recipient string `json:"recipient"` - Value string `json:"value"` - Gas string `json:"gas"` - GasPrice string `json:"gasprice"` - Init string `json:"init"` - Body string `json:"body"` -} - -// type TxResponse struct { -// Hash string -// } - -func (obj *NewTxArgs) UnmarshalJSON(b []byte) (err error) { - if err = json.Unmarshal(b, obj); err == nil { - return - } - return NewErrorResponse(ErrorDecodeArgs) -} - -func (a *NewTxArgs) requirements() error { - if a.Recipient == "" { - return NewErrorResponse("Transact requires a 'recipient' address as argument") - } - if a.Value == "" { - return NewErrorResponse("Transact requires a 'value' as argument") - } - if a.Gas == "" { - return NewErrorResponse("Transact requires a 'gas' value as argument") - } - if a.GasPrice == "" { - return NewErrorResponse("Transact requires a 'gasprice' value as argument") - } - return nil -} - -func (a *NewTxArgs) requirementsContract() error { - if a.Value == "" { - return NewErrorResponse("Create requires a 'value' as argument") - } - if a.Gas == "" { - return NewErrorResponse("Create requires a 'gas' value as argument") - } - if a.GasPrice == "" { - return NewErrorResponse("Create requires a 'gasprice' value as argument") - } - if a.Body == "" { - return NewErrorResponse("Create requires a 'body' value as argument") - } - return nil + From string `json:"from"` + To string `json:"to"` + Value string `json:"value"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Data string `json:"data"` } type PushTxArgs struct { @@ -104,10 +59,28 @@ func (a *PushTxArgs) requirementsPushTx() error { type GetStorageArgs struct { Address string - Key string } func (obj *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { + if err = json.Unmarshal(b, &obj.Address); err != nil { + return NewErrorResponse(ErrorDecodeArgs) + } + return +} + +func (a *GetStorageArgs) requirements() error { + if len(a.Address) == 0 { + return NewErrorResponse("GetStorageAt requires an 'address' value as argument") + } + return nil +} + +type GetStateArgs struct { + Address string + Key string +} + +func (obj *GetStateArgs) UnmarshalJSON(b []byte) (err error) { arg0 := "" if err = json.Unmarshal(b, arg0); err == nil { obj.Address = arg0 @@ -116,7 +89,7 @@ func (obj *GetStorageArgs) UnmarshalJSON(b []byte) (err error) { return NewErrorResponse(ErrorDecodeArgs) } -func (a *GetStorageArgs) requirements() error { +func (a *GetStateArgs) requirements() error { if a.Address == "" { return NewErrorResponse("GetStorageAt requires an 'address' value as argument") } @@ -127,9 +100,8 @@ func (a *GetStorageArgs) requirements() error { } type GetStorageAtRes struct { - Key string `json:"key"` - Value string `json:"value"` - Address string `json:"address"` + Key string `json:"key"` + Value string `json:"value"` } type GetTxCountArgs struct { @@ -216,3 +188,65 @@ func (a *GetCodeAtArgs) requirements() error { } return nil } + +type Sha3Args struct { + Data string +} + +func (obj *Sha3Args) UnmarshalJSON(b []byte) (err error) { + if err = json.Unmarshal(b, &obj.Data); err != nil { + return NewErrorResponse(ErrorDecodeArgs) + } + return +} + +type FilterOptions struct { + Earliest int64 + Latest int64 + Address string + Topic []string + Skip int + Max int +} + +func toFilterOptions(options *FilterOptions) core.FilterOptions { + var opts core.FilterOptions + opts.Earliest = options.Earliest + opts.Latest = options.Latest + opts.Address = fromHex(options.Address) + opts.Topics = make([][]byte, len(options.Topic)) + for i, topic := range options.Topic { + opts.Topics[i] = fromHex(topic) + } + + return opts +} + +type FilterChangedArgs struct { + n int +} + +type DbArgs struct { + Database string + Key string + Value string +} + +func (a *DbArgs) requirements() error { + if len(a.Database) == 0 { + return NewErrorResponse("DbPutArgs requires an 'Database' value as argument") + } + if len(a.Key) == 0 { + return NewErrorResponse("DbPutArgs requires an 'Key' value as argument") + } + return nil +} + +type WhisperMessageArgs struct { + Payload string + To string + From string + Topics []string + Priority uint32 + Ttl uint32 +} diff --git a/rpc/http/server.go b/rpc/http/server.go index 965727a4e..a34400a77 100644 --- a/rpc/http/server.go +++ b/rpc/http/server.go @@ -84,27 +84,30 @@ func (s *RpcHttpServer) Start() { } func (s *RpcHttpServer) apiHandler(api *rpc.EthereumApi) http.Handler { + var jsonrpcver string = "2.0" fn := func(w http.ResponseWriter, req *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") - rpchttplogger.Debugln("Handling request") + rpchttplogger.DebugDetailln("Handling request") reqParsed, reqerr := JSON.ParseRequestBody(req) if reqerr != nil { - JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: true, ErrorText: rpc.ErrorParseRequest}) + jsonerr := &rpc.RpcErrorObject{-32700, rpc.ErrorParseRequest} + JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) return } var response interface{} reserr := api.GetRequestReply(&reqParsed, &response) if reserr != nil { - rpchttplogger.Errorln(reserr) - JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: true, ErrorText: reserr.Error()}) + rpchttplogger.Warnln(reserr) + jsonerr := &rpc.RpcErrorObject{-32603, reserr.Error()} + JSON.Send(w, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: &reqParsed.ID, Error: jsonerr}) return } - rpchttplogger.Debugf("Generated response: %T %s", response, response) - JSON.Send(w, &rpc.RpcSuccessResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: false, Result: response}) + rpchttplogger.DebugDetailf("Generated response: %T %s", response, response) + JSON.Send(w, &rpc.RpcSuccessResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Result: response}) } return http.HandlerFunc(fn) diff --git a/rpc/message.go b/rpc/message.go index caf50a6c0..78dc6e2ff 100644 --- a/rpc/message.go +++ b/rpc/message.go @@ -20,6 +20,9 @@ import ( "bytes" "encoding/json" "errors" + "fmt" + + "github.com/ethereum/go-ethereum/xeth" ) const ( @@ -30,30 +33,51 @@ const ( ErrorDecodeArgs = "Error: Could not decode arguments" ) -type ErrorResponse struct { - 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"` } type RpcSuccessResponse struct { ID int `json:"id"` JsonRpc string `json:"jsonrpc"` - Error bool `json:"error"` Result interface{} `json:"result"` } type RpcErrorResponse struct { - ID int `json:"id"` - JsonRpc string `json:"jsonrpc"` - Error bool `json:"error"` - ErrorText string `json:"errortext"` + ID *int `json:"id"` + JsonRpc string `json:"jsonrpc"` + Error *RpcErrorObject `json:"error"` } -type RpcRequest struct { - JsonRpc string `json:"jsonrpc"` - ID int `json:"id"` - Method string `json:"method"` - Params []json.RawMessage `json:"params"` +type RpcErrorObject struct { + Code int `json:"code"` + Message string `json:"message"` + // Data interface{} `json:"data"` +} + +func NewErrorResponse(msg string) error { + return errors.New(msg) +} + +func NewErrorResponseWithError(msg string, err error) error { + return fmt.Errorf("%s: %v", msg, err) +} + +func (req *RpcRequest) ToSha3Args() (*Sha3Args, error) { + if len(req.Params) < 1 { + return nil, NewErrorResponse(ErrorArguments) + } + + args := new(Sha3Args) + r := bytes.NewReader(req.Params[0]) + if err := json.NewDecoder(r).Decode(args); err != nil { + return nil, NewErrorResponse(ErrorDecodeArgs) + } + rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } func (req *RpcRequest) ToGetBlockArgs() (*GetBlockArgs, error) { @@ -72,7 +96,7 @@ func (req *RpcRequest) ToGetBlockArgs() (*GetBlockArgs, error) { } func (req *RpcRequest) ToNewTxArgs() (*NewTxArgs, error) { - if len(req.Params) < 7 { + if len(req.Params) < 1 { return nil, NewErrorResponse(ErrorArguments) } @@ -80,7 +104,7 @@ func (req *RpcRequest) ToNewTxArgs() (*NewTxArgs, error) { r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) if err != nil { - return nil, NewErrorResponse(ErrorDecodeArgs) + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) } rpclogger.DebugDetailf("%T %v", args, args) return args, nil @@ -101,12 +125,12 @@ func (req *RpcRequest) ToPushTxArgs() (*PushTxArgs, error) { return args, nil } -func (req *RpcRequest) ToGetStorageArgs() (*GetStorageArgs, error) { - if len(req.Params) < 2 { +func (req *RpcRequest) ToGetStateArgs() (*GetStateArgs, error) { + if len(req.Params) < 1 { return nil, NewErrorResponse(ErrorArguments) } - args := new(GetStorageArgs) + args := new(GetStateArgs) // TODO need to pass both arguments r := bytes.NewReader(req.Params[0]) err := json.NewDecoder(r).Decode(args) @@ -117,6 +141,21 @@ func (req *RpcRequest) ToGetStorageArgs() (*GetStorageArgs, error) { return args, nil } +func (req *RpcRequest) ToStorageAtArgs() (*GetStorageArgs, error) { + if len(req.Params) < 1 { + return nil, NewErrorResponse(ErrorArguments) + } + + args := new(GetStorageArgs) + r := bytes.NewReader(req.Params[0]) + err := json.NewDecoder(r).Decode(args) + if err != nil { + return nil, NewErrorResponse(ErrorDecodeArgs) + } + rpclogger.DebugDetailf("%T %v", args, args) + return args, nil +} + func (req *RpcRequest) ToGetTxCountArgs() (*GetTxCountArgs, error) { if len(req.Params) < 1 { return nil, NewErrorResponse(ErrorArguments) @@ -162,6 +201,144 @@ func (req *RpcRequest) ToGetCodeAtArgs() (*GetCodeAtArgs, error) { return args, nil } -func NewErrorResponse(msg string) error { - return errors.New(msg) +func (req *RpcRequest) ToFilterArgs() (*FilterOptions, error) { + if len(req.Params) < 1 { + return nil, NewErrorResponse(ErrorArguments) + } + + args := new(FilterOptions) + r := bytes.NewReader(req.Params[0]) + err := json.NewDecoder(r).Decode(args) + if err != nil { + return nil, NewErrorResponse(ErrorDecodeArgs) + } + rpclogger.DebugDetailf("%T %v", args, args) + return args, nil +} + +func (req *RpcRequest) ToFilterStringArgs() (string, error) { + if len(req.Params) < 1 { + return "", NewErrorResponse(ErrorArguments) + } + + var args string + err := json.Unmarshal(req.Params[0], &args) + if err != nil { + return "", NewErrorResponse(ErrorDecodeArgs) + } + + rpclogger.DebugDetailf("%T %v", args, args) + return args, nil +} + +func (req *RpcRequest) ToFilterChangedArgs() (int, error) { + if len(req.Params) < 1 { + return 0, NewErrorResponse(ErrorArguments) + } + + var id int + r := bytes.NewReader(req.Params[0]) + err := json.NewDecoder(r).Decode(&id) + if err != nil { + return 0, NewErrorResponse(ErrorDecodeArgs) + } + rpclogger.DebugDetailf("%T %v", id, id) + return id, nil +} + +func (req *RpcRequest) ToDbPutArgs() (*DbArgs, error) { + if len(req.Params) < 3 { + return nil, NewErrorResponse(ErrorArguments) + } + + var args DbArgs + err := json.Unmarshal(req.Params[0], &args.Database) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + err = json.Unmarshal(req.Params[1], &args.Key) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + err = json.Unmarshal(req.Params[2], &args.Value) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil +} + +func (req *RpcRequest) ToDbGetArgs() (*DbArgs, error) { + if len(req.Params) < 2 { + return nil, NewErrorResponse(ErrorArguments) + } + + var args DbArgs + err := json.Unmarshal(req.Params[0], &args.Database) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + + err = json.Unmarshal(req.Params[1], &args.Key) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil +} + +func (req *RpcRequest) ToWhisperFilterArgs() (*xeth.Options, error) { + if len(req.Params) < 1 { + return nil, NewErrorResponse(ErrorArguments) + } + + var args xeth.Options + err := json.Unmarshal(req.Params[0], &args) + if err != nil { + return nil, NewErrorResponseWithError(ErrorDecodeArgs, err) + } + rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil +} + +func (req *RpcRequest) ToWhisperIdArgs() (int, error) { + if len(req.Params) < 1 { + return 0, NewErrorResponse(ErrorArguments) + } + + var id int + err := json.Unmarshal(req.Params[0], &id) + if err != nil { + return 0, NewErrorResponse(ErrorDecodeArgs) + } + rpclogger.DebugDetailf("%T %v", id, id) + return id, nil +} + +func (req *RpcRequest) ToWhisperPostArgs() (*WhisperMessageArgs, error) { + if len(req.Params) < 1 { + return nil, NewErrorResponse(ErrorArguments) + } + + var args WhisperMessageArgs + err := json.Unmarshal(req.Params[0], &args) + if err != nil { + return nil, err + } + rpclogger.DebugDetailf("%T %v", args, args) + return &args, nil +} + +func (req *RpcRequest) ToWhisperHasIdentityArgs() (string, error) { + if len(req.Params) < 1 { + return "", NewErrorResponse(ErrorArguments) + } + + var args string + err := json.Unmarshal(req.Params[0], &args) + if err != nil { + return "", err + } + rpclogger.DebugDetailf("%T %v", args, args) + return args, nil } diff --git a/rpc/packages.go b/rpc/packages.go index 700b9b2b3..ac3127356 100644 --- a/rpc/packages.go +++ b/rpc/packages.go @@ -26,24 +26,104 @@ For each request type, define the following: package rpc import ( + "fmt" "math/big" "strings" + "sync" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/event/filter" + "github.com/ethereum/go-ethereum/state" "github.com/ethereum/go-ethereum/xeth" ) -type RpcServer interface { - Start() - Stop() +const ( + defaultGasPrice = "10000000000000" + defaultGas = "10000" +) + +type EthereumApi struct { + xeth *xeth.XEth + filterManager *filter.FilterManager + + logMut sync.RWMutex + logs map[int]state.Logs + + messagesMut sync.RWMutex + messages map[int][]xeth.WhisperMessage + + db ethutil.Database } -func NewEthereumApi(xeth *xeth.XEth) *EthereumApi { - return &EthereumApi{xeth: xeth} +func NewEthereumApi(eth *xeth.XEth) *EthereumApi { + db, _ := ethdb.NewLDBDatabase("dapps") + api := &EthereumApi{ + xeth: eth, + filterManager: filter.NewFilterManager(eth.Backend().EventMux()), + logs: make(map[int]state.Logs), + messages: make(map[int][]xeth.WhisperMessage), + db: db, + } + go api.filterManager.Start() + + return api } -type EthereumApi struct { - xeth *xeth.XEth +func (self *EthereumApi) NewFilter(args *FilterOptions, reply *interface{}) error { + var id int + filter := core.NewFilter(self.xeth.Backend()) + filter.SetOptions(toFilterOptions(args)) + filter.LogsCallback = func(logs state.Logs) { + self.logMut.Lock() + defer self.logMut.Unlock() + + self.logs[id] = append(self.logs[id], logs...) + } + id = self.filterManager.InstallFilter(filter) + *reply = id + + return nil +} + +func (self *EthereumApi) NewFilterString(args string, reply *interface{}) error { + var id int + filter := core.NewFilter(self.xeth.Backend()) + + callback := func(block *types.Block) { + self.logs[id] = append(self.logs[id], &state.StateLog{}) + } + if args == "pending" { + filter.PendingCallback = callback + } else if args == "chain" { + filter.BlockCallback = callback + } + + id = self.filterManager.InstallFilter(filter) + *reply = id + + return nil +} + +func (self *EthereumApi) FilterChanged(id int, reply *interface{}) error { + self.logMut.RLock() + defer self.logMut.RUnlock() + + *reply = toLogs(self.logs[id]) + + self.logs[id] = nil // empty the logs + + return nil +} + +func (self *EthereumApi) Logs(id int, reply *interface{}) error { + filter := self.filterManager.GetFilter(id) + *reply = toLogs(filter.Find()) + + return nil } func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { @@ -61,22 +141,25 @@ func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *interface{}) error { } func (p *EthereumApi) Transact(args *NewTxArgs, reply *interface{}) error { - err := args.requirements() - if err != nil { - return err + if len(args.Gas) == 0 { + args.Gas = defaultGas + } + + if len(args.GasPrice) == 0 { + args.GasPrice = defaultGasPrice } - result, _ := p.xeth.Transact( /* TODO specify account */ "", args.Recipient, args.Value, args.Gas, args.GasPrice, args.Body) + + result, _ := p.xeth.Transact( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data) *reply = result return nil } -func (p *EthereumApi) Create(args *NewTxArgs, reply *interface{}) error { - err := args.requirementsContract() +func (p *EthereumApi) Call(args *NewTxArgs, reply *interface{}) error { + result, err := p.xeth.Call( /* TODO specify account */ args.To, args.Value, args.Gas, args.GasPrice, args.Data) if err != nil { return err } - result, _ := p.xeth.Transact( /* TODO specify account */ "", "", args.Value, args.Gas, args.GasPrice, args.Body) *reply = result return nil } @@ -91,7 +174,7 @@ func (p *EthereumApi) PushTx(args *PushTxArgs, reply *interface{}) error { return nil } -func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) error { +func (p *EthereumApi) GetStateAt(args *GetStateArgs, reply *interface{}) error { err := args.requirements() if err != nil { return err @@ -99,6 +182,7 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) err state := p.xeth.State().SafeGet(args.Address) + value := state.StorageString(args.Key) var hx string if strings.Index(args.Key, "0x") == 0 { hx = string([]byte(args.Key)[2:]) @@ -107,9 +191,18 @@ func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) err i, _ := new(big.Int).SetString(args.Key, 10) hx = ethutil.Bytes2Hex(i.Bytes()) } - rpclogger.Debugf("GetStorageAt(%s, %s)\n", args.Address, hx) - value := state.Storage(ethutil.Hex2Bytes(hx)) - *reply = GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value.Str()} + rpclogger.Debugf("GetStateAt(%s, %s)\n", args.Address, hx) + *reply = map[string]string{args.Key: value.Str()} + return nil +} + +func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *interface{}) error { + err := args.requirements() + if err != nil { + return err + } + + *reply = p.xeth.State().SafeGet(args.Address).Storage() return nil } @@ -128,11 +221,21 @@ func (p *EthereumApi) GetCoinbase(reply *interface{}) error { return nil } +func (p *EthereumApi) Accounts(reply *interface{}) error { + *reply = p.xeth.Accounts() + return nil +} + func (p *EthereumApi) GetIsMining(reply *interface{}) error { *reply = p.xeth.IsMining() return nil } +func (p *EthereumApi) BlockNumber(reply *interface{}) error { + *reply = p.xeth.Backend().ChainManager().CurrentBlock().Number() + return nil +} + func (p *EthereumApi) GetTxCountAt(args *GetTxCountArgs, reply *interface{}) error { err := args.requirements() if err != nil { @@ -148,7 +251,7 @@ func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *interface{}) err return err } state := p.xeth.State().SafeGet(args.Address) - *reply = BalanceRes{Balance: state.Balance().String(), Address: args.Address} + *reply = toHex(state.Balance().Bytes()) return nil } @@ -161,6 +264,81 @@ func (p *EthereumApi) GetCodeAt(args *GetCodeAtArgs, reply *interface{}) error { return nil } +func (p *EthereumApi) Sha3(args *Sha3Args, reply *interface{}) error { + *reply = toHex(crypto.Sha3(fromHex(args.Data))) + return nil +} + +func (p *EthereumApi) DbPut(args *DbArgs, reply *interface{}) error { + err := args.requirements() + if err != nil { + return err + } + + p.db.Put([]byte(args.Database+args.Key), []byte(args.Value)) + *reply = true + return nil +} + +func (p *EthereumApi) DbGet(args *DbArgs, reply *interface{}) error { + err := args.requirements() + if err != nil { + return err + } + + res, _ := p.db.Get([]byte(args.Database + args.Key)) + *reply = string(res) + return nil +} + +func (p *EthereumApi) NewWhisperIdentity(reply *interface{}) error { + *reply = p.xeth.Whisper().NewIdentity() + return nil +} + +func (p *EthereumApi) NewWhisperFilter(args *xeth.Options, reply *interface{}) error { + var id int + args.Fn = func(msg xeth.WhisperMessage) { + p.messagesMut.Lock() + defer p.messagesMut.Unlock() + p.messages[id] = append(p.messages[id], msg) + } + id = p.xeth.Whisper().Watch(args) + *reply = id + return nil +} + +func (self *EthereumApi) MessagesChanged(id int, reply *interface{}) error { + self.messagesMut.RLock() + defer self.messagesMut.RUnlock() + + *reply = self.messages[id] + + self.messages[id] = nil // empty the messages + + return nil +} + +func (p *EthereumApi) WhisperPost(args *WhisperMessageArgs, reply *interface{}) error { + err := p.xeth.Whisper().Post(args.Payload, args.To, args.From, args.Topics, args.Priority, args.Ttl) + if err != nil { + return err + } + + *reply = true + return nil +} + +func (p *EthereumApi) HasWhisperIdentity(args string, reply *interface{}) error { + *reply = p.xeth.Whisper().HasIdentity(args) + return nil +} + +func (p *EthereumApi) WhisperMessages(id int, reply *interface{}) error { + *reply = p.xeth.Whisper().Messages(id) + return nil +} + func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { // Spec at https://github.com/ethereum/wiki/wiki/Generic-ON-RPC rpclogger.DebugDetailf("%T %s", req.Params, req.Params) @@ -173,6 +351,10 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return p.GetIsMining(reply) case "eth_peerCount": return p.GetPeerCount(reply) + case "eth_number": + return p.BlockNumber(reply) + case "eth_accounts": + return p.Accounts(reply) case "eth_countAt": args, err := req.ToGetTxCountArgs() if err != nil { @@ -192,7 +374,13 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error } return p.GetBalanceAt(args, reply) case "eth_stateAt": - args, err := req.ToGetStorageArgs() + args, err := req.ToGetStateArgs() + if err != nil { + return err + } + return p.GetStateAt(args, reply) + case "eth_storageAt": + args, err := req.ToStorageAtArgs() if err != nil { return err } @@ -203,8 +391,91 @@ func (p *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error return err } return p.GetBlock(args, reply) + case "eth_transact": + args, err := req.ToNewTxArgs() + if err != nil { + return err + } + return p.Transact(args, reply) + case "eth_call": + args, err := req.ToNewTxArgs() + if err != nil { + return err + } + return p.Call(args, reply) + case "eth_newFilter": + args, err := req.ToFilterArgs() + if err != nil { + return err + } + return p.NewFilter(args, reply) + case "eth_newFilterString": + args, err := req.ToFilterStringArgs() + if err != nil { + return err + } + return p.NewFilterString(args, reply) + case "eth_changed": + args, err := req.ToFilterChangedArgs() + if err != nil { + return err + } + return p.FilterChanged(args, reply) + case "eth_gasPrice": + *reply = defaultGasPrice + return nil + case "web3_sha3": + args, err := req.ToSha3Args() + if err != nil { + return err + } + return p.Sha3(args, reply) + case "db_put": + args, err := req.ToDbPutArgs() + if err != nil { + return err + } + return p.DbPut(args, reply) + case "db_get": + args, err := req.ToDbGetArgs() + if err != nil { + return err + } + return p.DbGet(args, reply) + case "shh_newIdentity": + return p.NewWhisperIdentity(reply) + case "shh_newFilter": + args, err := req.ToWhisperFilterArgs() + if err != nil { + return err + } + return p.NewWhisperFilter(args, reply) + case "shh_changed": + args, err := req.ToWhisperIdArgs() + if err != nil { + return err + } + return p.MessagesChanged(args, reply) + case "shh_post": + args, err := req.ToWhisperPostArgs() + if err != nil { + return err + } + return p.WhisperPost(args, reply) + case "shh_haveIdentity": + args, err := req.ToWhisperHasIdentityArgs() + if err != nil { + return err + } + return p.HasWhisperIdentity(args, reply) + case "shh_getMessages": + args, err := req.ToWhisperIdArgs() + if err != nil { + return err + } + return p.WhisperMessages(args, reply) default: - return NewErrorResponse(ErrorNotImplemented) + return NewErrorResponse(fmt.Sprintf("%v %s", ErrorNotImplemented, req.Method)) } rpclogger.DebugDetailf("Reply: %T %s", reply, reply) diff --git a/rpc/json.go b/rpc/util.go index 9bd204818..509d9a17d 100644 --- a/rpc/json.go +++ b/rpc/util.go @@ -18,9 +18,12 @@ package rpc import ( "encoding/json" - "github.com/ethereum/go-ethereum/logger" "io" "net/http" + + "github.com/ethereum/go-ethereum/ethutil" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/state" ) var rpclogger = logger.NewLogger("RPC") @@ -34,7 +37,7 @@ func (self JsonWrapper) Send(writer io.Writer, v interface{}) (n int, err error) rpclogger.Fatalln("Error marshalling JSON", err) return 0, err } - rpclogger.Infof("Sending payload: %s", payload) + rpclogger.DebugDetailf("Sending payload: %s", payload) return writer.Write(payload) } @@ -56,3 +59,44 @@ func (self JsonWrapper) ParseRequestBody(req *http.Request) (RpcRequest, error) return reqParsed, nil } + +func toHex(b []byte) string { + return "0x" + ethutil.Bytes2Hex(b) +} +func fromHex(s string) []byte { + if len(s) > 1 { + if s[0:2] == "0x" { + s = s[2:] + } + return ethutil.Hex2Bytes(s) + } + return nil +} + +type RpcServer interface { + Start() + Stop() +} + +type Log struct { + Address string `json:"address"` + Topics []string `json:"topics"` + Data string `json:"data"` +} + +func toLogs(logs state.Logs) (ls []Log) { + ls = make([]Log, len(logs)) + + for i, log := range logs { + var l Log + l.Topics = make([]string, len(log.Topics())) + l.Address = toHex(log.Address()) + l.Data = toHex(log.Data()) + for j, topic := range log.Topics() { + l.Topics[j] = toHex(topic) + } + ls[i] = l + } + + return +} diff --git a/rpc/ws/server.go b/rpc/ws/server.go index 2efc6ff02..fe6af042f 100644 --- a/rpc/ws/server.go +++ b/rpc/ws/server.go @@ -22,8 +22,6 @@ import ( "net/http" "code.google.com/p/go.net/websocket" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/event/filter" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/xeth" @@ -33,25 +31,21 @@ var wslogger = logger.NewLogger("RPC-WS") var JSON rpc.JsonWrapper type WebSocketServer struct { - eth *eth.Ethereum - filterManager *filter.FilterManager - port int - doneCh chan bool - listener net.Listener + pipe *xeth.XEth + port int + doneCh chan bool + listener net.Listener } -func NewWebSocketServer(eth *eth.Ethereum, port int) (*WebSocketServer, error) { +func NewWebSocketServer(pipe *xeth.XEth, port int) (*WebSocketServer, error) { sport := fmt.Sprintf(":%d", port) l, err := net.Listen("tcp", sport) if err != nil { return nil, err } - filterManager := filter.NewFilterManager(eth.EventMux()) - go filterManager.Start() - - return &WebSocketServer{eth, - filterManager, + return &WebSocketServer{ + pipe, port, make(chan bool), l, @@ -76,7 +70,7 @@ func (self *WebSocketServer) Start() { wslogger.Infof("Starting RPC-WS server on port %d", self.port) go self.handlerLoop() - api := rpc.NewEthereumApi(xeth.New(self.eth)) + api := rpc.NewEthereumApi(self.pipe) h := self.apiHandler(api) http.Handle("/ws", h) @@ -97,6 +91,7 @@ func (s *WebSocketServer) apiHandler(api *rpc.EthereumApi) http.Handler { } func sockHandler(api *rpc.EthereumApi) websocket.Handler { + var jsonrpcver string = "2.0" fn := func(conn *websocket.Conn) { for { wslogger.Debugln("Handling connection") @@ -104,23 +99,22 @@ func sockHandler(api *rpc.EthereumApi) websocket.Handler { // reqParsed, reqerr := JSON.ParseRequestBody(conn.Request()) if err := websocket.JSON.Receive(conn, &reqParsed); err != nil { - // if reqerr != nil { - JSON.Send(conn, &rpc.RpcErrorResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: true, ErrorText: rpc.ErrorParseRequest}) - // websocket.JSON.Send(conn, rpc.RpcErrorResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: true, ErrorText: rpc.ErrorParseRequest}) + jsonerr := &rpc.RpcErrorObject{-32700, rpc.ErrorParseRequest} + JSON.Send(conn, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: nil, Error: jsonerr}) continue } var response interface{} reserr := api.GetRequestReply(&reqParsed, &response) if reserr != nil { - // websocket.JSON.Send(conn, &rpc.RpcErrorResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: true, ErrorText: reserr.Error()}) - JSON.Send(conn, &rpc.RpcErrorResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: true, ErrorText: reserr.Error()}) + wslogger.Warnln(reserr) + jsonerr := &rpc.RpcErrorObject{-32603, reserr.Error()} + JSON.Send(conn, &rpc.RpcErrorResponse{JsonRpc: jsonrpcver, ID: &reqParsed.ID, Error: jsonerr}) continue } wslogger.Debugf("Generated response: %T %s", response, response) - JSON.Send(conn, &rpc.RpcSuccessResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: false, Result: response}) - // websocket.JSON.Send(conn, rpc.RpcSuccessResponse{JsonRpc: reqParsed.JsonRpc, ID: reqParsed.ID, Error: false, Result: response}) + JSON.Send(conn, &rpc.RpcSuccessResponse{JsonRpc: jsonrpcver, ID: reqParsed.ID, Result: response}) } } return websocket.Handler(fn) |