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  }