github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/rpc/jeth.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "encoding/json" 21 "fmt" 22 23 "github.com/ethereum/go-ethereum/cmd/utils" 24 "github.com/ethereum/go-ethereum/jsre" 25 "github.com/ethereum/go-ethereum/logger" 26 "github.com/ethereum/go-ethereum/logger/glog" 27 "github.com/ethereum/go-ethereum/rpc/comms" 28 "github.com/ethereum/go-ethereum/rpc/shared" 29 "github.com/ethereum/go-ethereum/rpc/useragent" 30 "github.com/ethereum/go-ethereum/xeth" 31 32 "github.com/robertkrimen/otto" 33 ) 34 35 type Jeth struct { 36 ethApi shared.EthereumApi 37 re *jsre.JSRE 38 client comms.EthereumClient 39 fe xeth.Frontend 40 } 41 42 func NewJeth(ethApi shared.EthereumApi, re *jsre.JSRE, client comms.EthereumClient, fe xeth.Frontend) *Jeth { 43 return &Jeth{ethApi, re, client, fe} 44 } 45 46 func (self *Jeth) err(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) { 47 errObj := fmt.Sprintf("{\"message\": \"%s\", \"code\": %d}", msg, code) 48 retResponse := fmt.Sprintf("ret_response = JSON.parse('{\"jsonrpc\": \"%s\", \"id\": %v, \"error\": %s}');", shared.JsonRpcVersion, id, errObj) 49 50 call.Otto.Run("ret_error = " + errObj) 51 res, _ := call.Otto.Run(retResponse) 52 53 return res 54 } 55 56 func (self *Jeth) Send(call otto.FunctionCall) (response otto.Value) { 57 reqif, err := call.Argument(0).Export() 58 if err != nil { 59 return self.err(call, -32700, err.Error(), nil) 60 } 61 62 jsonreq, err := json.Marshal(reqif) 63 var reqs []shared.Request 64 batch := true 65 err = json.Unmarshal(jsonreq, &reqs) 66 if err != nil { 67 reqs = make([]shared.Request, 1) 68 err = json.Unmarshal(jsonreq, &reqs[0]) 69 batch = false 70 } 71 72 call.Otto.Set("response_len", len(reqs)) 73 call.Otto.Run("var ret_response = new Array(response_len);") 74 75 for i, req := range reqs { 76 var respif interface{} 77 err := self.client.Send(&req) 78 if err != nil { 79 return self.err(call, -32603, err.Error(), req.Id) 80 } 81 82 recv: 83 respif, err = self.client.Recv() 84 if err != nil { 85 return self.err(call, -32603, err.Error(), req.Id) 86 } 87 88 agentreq, isRequest := respif.(*shared.Request) 89 if isRequest { 90 self.handleRequest(agentreq) 91 goto recv // receive response after agent interaction 92 } 93 94 sucres, isSuccessResponse := respif.(*shared.SuccessResponse) 95 errres, isErrorResponse := respif.(*shared.ErrorResponse) 96 if !isSuccessResponse && !isErrorResponse { 97 return self.err(call, -32603, fmt.Sprintf("Invalid response type (%T)", respif), req.Id) 98 } 99 100 call.Otto.Set("ret_jsonrpc", shared.JsonRpcVersion) 101 call.Otto.Set("ret_id", req.Id) 102 103 var res []byte 104 if isSuccessResponse { 105 res, err = json.Marshal(sucres.Result) 106 } else if isErrorResponse { 107 res, err = json.Marshal(errres.Error) 108 } 109 110 call.Otto.Set("ret_result", string(res)) 111 call.Otto.Set("response_idx", i) 112 response, err = call.Otto.Run(` 113 ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) }; 114 `) 115 } 116 117 if !batch { 118 call.Otto.Run("ret_response = ret_response[0];") 119 } 120 121 if call.Argument(1).IsObject() { 122 call.Otto.Set("callback", call.Argument(1)) 123 call.Otto.Run(` 124 if (Object.prototype.toString.call(callback) == '[object Function]') { 125 callback(null, ret_response); 126 } 127 `) 128 } 129 130 return 131 } 132 133 // handleRequest will handle user agent requests by interacting with the user and sending 134 // the user response back to the geth service 135 func (self *Jeth) handleRequest(req *shared.Request) bool { 136 var err error 137 var args []interface{} 138 if err = json.Unmarshal(req.Params, &args); err != nil { 139 glog.V(logger.Info).Infof("Unable to parse agent request - %v\n", err) 140 return false 141 } 142 143 switch req.Method { 144 case useragent.AskPasswordMethod: 145 return self.askPassword(req.Id, req.Jsonrpc, args) 146 case useragent.ConfirmTransactionMethod: 147 return self.confirmTransaction(req.Id, req.Jsonrpc, args) 148 } 149 150 return false 151 } 152 153 // askPassword will ask the user to supply the password for a given account 154 func (self *Jeth) askPassword(id interface{}, jsonrpc string, args []interface{}) bool { 155 var err error 156 var passwd string 157 if len(args) >= 1 { 158 if account, ok := args[0].(string); ok { 159 fmt.Printf("Unlock account %s\n", account) 160 passwd, err = utils.PromptPassword("Passphrase: ", true) 161 } else { 162 return false 163 } 164 } 165 166 if err = self.client.Send(shared.NewRpcResponse(id, jsonrpc, passwd, err)); err != nil { 167 glog.V(logger.Info).Infof("Unable to send user agent ask password response - %v\n", err) 168 } 169 170 return err == nil 171 } 172 173 func (self *Jeth) confirmTransaction(id interface{}, jsonrpc string, args []interface{}) bool { 174 // Accept all tx which are send from this console 175 return self.client.Send(shared.NewRpcResponse(id, jsonrpc, true, nil)) == nil 176 }