github.com/igggame/nebulas-go@v2.1.0+incompatible/cmd/console/jsbridge.go (about) 1 // Copyright (C) 2017 go-nebulas authors 2 // 3 // This file is part of the go-nebulas library. 4 // 5 // the go-nebulas library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // the go-nebulas library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with the go-nebulas library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 19 package console 20 21 import ( 22 "errors" 23 "fmt" 24 "io" 25 "strings" 26 27 "bytes" 28 "io/ioutil" 29 "net/http" 30 31 "github.com/nebulasio/go-nebulas/neblet/pb" 32 "github.com/robertkrimen/otto" 33 ) 34 35 const ( 36 //APIVersion rpc http version 37 APIVersion = "v1" 38 ) 39 40 type jsBridge struct { 41 42 // js request host 43 host string 44 45 // terminal input prompter 46 prompter UserPrompter 47 48 writer io.Writer 49 } 50 51 // newBirdge create a new jsbridge with given prompter and writer 52 func newBirdge(config *nebletpb.Config, prompter UserPrompter, writer io.Writer) *jsBridge { 53 bridge := &jsBridge{prompter: prompter, writer: writer} 54 if config.GetRpc() != nil { 55 bridge.host = config.GetRpc().HttpListen[0] 56 if !strings.HasPrefix(bridge.host, "http") { 57 bridge.host = "http://" + bridge.host 58 } 59 } else { 60 bridge.host = "http://localhost:8685" 61 } 62 return bridge 63 } 64 65 // output handle the error & log in js runtime 66 func (b *jsBridge) output(call otto.FunctionCall) { 67 output := []string{} 68 for _, argument := range call.ArgumentList { 69 output = append(output, fmt.Sprintf("%v", argument)) 70 } 71 fmt.Fprintln(b.writer, strings.Join(output, " ")) 72 } 73 74 // setHost update repl request host 75 func (b *jsBridge) setHost(call otto.FunctionCall) otto.Value { 76 host := call.Argument(0) 77 if !host.IsString() { 78 return jsError(call.Otto, errors.New("setHost host is null")) 79 } 80 b.host = host.String() 81 return otto.NullValue() 82 } 83 84 // request handle http request 85 func (b *jsBridge) request(call otto.FunctionCall) otto.Value { 86 method := call.Argument(0) 87 api := call.Argument(1) 88 if method.IsNull() || api.IsNull() { 89 return jsError(call.Otto, errors.New("request method/api is null")) 90 } 91 92 // convert args to string 93 JSON, _ := call.Otto.Object("JSON") 94 args := "" 95 if !call.Argument(2).IsNull() { 96 argsVal, err := JSON.Call("stringify", call.Argument(2)) 97 if err != nil { 98 return jsError(call.Otto, err) 99 } 100 if argsVal.IsString() { 101 args = argsVal.String() 102 } 103 } 104 105 url := b.host + "/" + APIVersion + api.String() 106 //fmt.Fprintln(b.writer, "request", url, method.String(), args) 107 // method only support upper case. 108 req, err := http.NewRequest(strings.ToUpper(method.String()), url, bytes.NewBuffer([]byte(args))) 109 if err != nil { 110 return jsError(call.Otto, err) 111 } 112 req.Header.Set("Content-Type", "application/json") 113 114 client := &http.Client{} 115 resp, err := client.Do(req) 116 if err != nil { 117 return jsError(call.Otto, err) 118 } 119 120 defer resp.Body.Close() 121 result, err := ioutil.ReadAll(resp.Body) 122 if err != nil { 123 return jsError(call.Otto, err) 124 } 125 //fmt.Fprintln(b.writer, "result:", result) 126 response, err := JSON.Call("parse", string(result)) 127 if err != nil { 128 // if result can't be parse to json obj ,return origin string 129 response, _ = otto.ToValue(string(result)) 130 } 131 132 if fn := call.Argument(3); fn.Class() == "Function" { 133 fn.Call(otto.NullValue(), response) 134 return otto.UndefinedValue() 135 } 136 return response 137 } 138 139 // newAccount handle the account generate with passphrase input 140 func (b *jsBridge) newAccount(call otto.FunctionCall) otto.Value { 141 var ( 142 password string 143 err error 144 ) 145 switch { 146 // No password was specified, prompt the user for it 147 case len(call.ArgumentList) == 0: 148 if password, err = b.prompter.PromptPassphrase("Passphrase: "); err != nil { 149 fmt.Fprintln(b.writer, err) 150 return otto.NullValue() 151 } 152 var confirm string 153 if confirm, err = b.prompter.PromptPassphrase("Repeat passphrase: "); err != nil { 154 fmt.Fprintln(b.writer, err) 155 return otto.NullValue() 156 } 157 if password != confirm { 158 fmt.Fprintln(b.writer, errors.New("passphrase don't match")) 159 return otto.NullValue() 160 } 161 case len(call.ArgumentList) == 1 && call.Argument(0).IsString(): 162 password, _ = call.Argument(0).ToString() 163 default: 164 fmt.Fprintln(b.writer, errors.New("unexpected argument count")) 165 return otto.NullValue() 166 } 167 ret, err := call.Otto.Call("bridge.newAccount", nil, password) 168 if err != nil { 169 fmt.Fprintln(b.writer, err) 170 return otto.NullValue() 171 } 172 return ret 173 } 174 175 // signTransaction handle the account unlock with passphrase input 176 func (b *jsBridge) unlockAccount(call otto.FunctionCall) otto.Value { 177 if !call.Argument(0).IsString() { 178 fmt.Fprintln(b.writer, errors.New("address arg must be string")) 179 return otto.NullValue() 180 } 181 address := call.Argument(0) 182 183 var passphrase otto.Value 184 185 if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() { 186 fmt.Fprintf(b.writer, "Unlock account %s\n", address) 187 var ( 188 input string 189 err error 190 ) 191 if input, err = b.prompter.PromptPassphrase("Passphrase: "); err != nil { 192 fmt.Fprintln(b.writer, err) 193 return otto.NullValue() 194 } 195 passphrase, _ = otto.ToValue(input) 196 } else { 197 if !call.Argument(1).IsString() { 198 fmt.Fprintln(b.writer, errors.New("password must be a string")) 199 return otto.NullValue() 200 } 201 passphrase = call.Argument(1) 202 } 203 204 // Send the request to the backend and return 205 val, err := call.Otto.Call("bridge.unlockAccount", nil, address, passphrase) 206 if err != nil { 207 fmt.Fprintln(b.writer, err) 208 return otto.NullValue() 209 } 210 return val 211 } 212 213 // sendTransactionWithPassphrase handle the transaction send with passphrase input 214 func (b *jsBridge) sendTransactionWithPassphrase(call otto.FunctionCall) otto.Value { 215 if !call.Argument(0).IsString() || !call.Argument(1).IsString() { 216 fmt.Fprintln(b.writer, errors.New("from/to address arg must be string")) 217 return otto.NullValue() 218 } 219 var passphrase otto.Value 220 if call.Argument(8).IsUndefined() || call.Argument(8).IsNull() { 221 var ( 222 input string 223 err error 224 ) 225 if input, err = b.prompter.PromptPassphrase("Passphrase: "); err != nil { 226 fmt.Fprintln(b.writer, err) 227 return otto.NullValue() 228 } 229 passphrase, _ = otto.ToValue(input) 230 } else { 231 if !call.Argument(8).IsString() { 232 fmt.Fprintln(b.writer, errors.New("password must be a string")) 233 return otto.NullValue() 234 } 235 passphrase = call.Argument(1) 236 } 237 // Send the request to the backend and return 238 val, err := call.Otto.Call("bridge.sendTransactionWithPassphrase", nil, 239 call.Argument(0), call.Argument(1), call.Argument(2), 240 call.Argument(3), call.Argument(4), call.Argument(5), 241 call.Argument(6), call.Argument(7), passphrase) 242 if err != nil { 243 fmt.Fprintln(b.writer, err) 244 return otto.NullValue() 245 } 246 return val 247 } 248 249 // signTransactionWithPassphrase handle the transaction sign with passphrase input 250 func (b *jsBridge) signTransactionWithPassphrase(call otto.FunctionCall) otto.Value { 251 if !call.Argument(0).IsString() || !call.Argument(1).IsString() { 252 fmt.Fprintln(b.writer, errors.New("from/to address arg must be string")) 253 return otto.NullValue() 254 } 255 var passphrase otto.Value 256 if call.Argument(8).IsUndefined() || call.Argument(8).IsNull() { 257 var ( 258 input string 259 err error 260 ) 261 if input, err = b.prompter.PromptPassphrase("Passphrase: "); err != nil { 262 fmt.Fprintln(b.writer, err) 263 return otto.NullValue() 264 } 265 passphrase, _ = otto.ToValue(input) 266 } else { 267 if !call.Argument(8).IsString() { 268 fmt.Fprintln(b.writer, errors.New("password must be a string")) 269 return otto.NullValue() 270 } 271 passphrase = call.Argument(1) 272 } 273 // Send the request to the backend and return 274 val, err := call.Otto.Call("bridge.signTransactionWithPassphrase", nil, 275 call.Argument(0), call.Argument(1), call.Argument(2), 276 call.Argument(3), call.Argument(4), call.Argument(5), 277 call.Argument(6), call.Argument(7), passphrase) 278 if err != nil { 279 fmt.Fprintln(b.writer, err) 280 return otto.NullValue() 281 } 282 return val 283 } 284 285 func jsError(otto *otto.Otto, err error) otto.Value { 286 resp, _ := otto.Object(`({})`) 287 resp.Set("error", map[string]interface{}{"code": -1, "message": err.Error()}) 288 return resp.Value() 289 }