github.com/pslzym/go-ethereum@v1.8.17-0.20180926104442-4b6824e07b1b/signer/core/api_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU 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 // go-ethereum 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 General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 // 17 package core 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "io/ioutil" 24 "math/big" 25 "os" 26 "path/filepath" 27 "testing" 28 "time" 29 30 "github.com/ethereum/go-ethereum/accounts/keystore" 31 "github.com/ethereum/go-ethereum/cmd/utils" 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/common/hexutil" 34 "github.com/ethereum/go-ethereum/core/types" 35 "github.com/ethereum/go-ethereum/internal/ethapi" 36 "github.com/ethereum/go-ethereum/rlp" 37 ) 38 39 //Used for testing 40 type HeadlessUI struct { 41 controller chan string 42 } 43 44 func (ui *HeadlessUI) OnSignerStartup(info StartupInfo) { 45 } 46 47 func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) { 48 fmt.Printf("OnApproved()\n") 49 } 50 51 func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { 52 53 switch <-ui.controller { 54 case "Y": 55 return SignTxResponse{request.Transaction, true, <-ui.controller}, nil 56 case "M": //Modify 57 old := big.Int(request.Transaction.Value) 58 newVal := big.NewInt(0).Add(&old, big.NewInt(1)) 59 request.Transaction.Value = hexutil.Big(*newVal) 60 return SignTxResponse{request.Transaction, true, <-ui.controller}, nil 61 default: 62 return SignTxResponse{request.Transaction, false, ""}, nil 63 } 64 } 65 66 func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) { 67 if "Y" == <-ui.controller { 68 return SignDataResponse{true, <-ui.controller}, nil 69 } 70 return SignDataResponse{false, ""}, nil 71 } 72 73 func (ui *HeadlessUI) ApproveExport(request *ExportRequest) (ExportResponse, error) { 74 return ExportResponse{<-ui.controller == "Y"}, nil 75 76 } 77 78 func (ui *HeadlessUI) ApproveImport(request *ImportRequest) (ImportResponse, error) { 79 if "Y" == <-ui.controller { 80 return ImportResponse{true, <-ui.controller, <-ui.controller}, nil 81 } 82 return ImportResponse{false, "", ""}, nil 83 } 84 85 func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error) { 86 switch <-ui.controller { 87 case "A": 88 return ListResponse{request.Accounts}, nil 89 case "1": 90 l := make([]Account, 1) 91 l[0] = request.Accounts[1] 92 return ListResponse{l}, nil 93 default: 94 return ListResponse{nil}, nil 95 } 96 } 97 98 func (ui *HeadlessUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) { 99 if "Y" == <-ui.controller { 100 return NewAccountResponse{true, <-ui.controller}, nil 101 } 102 return NewAccountResponse{false, ""}, nil 103 } 104 105 func (ui *HeadlessUI) ShowError(message string) { 106 //stdout is used by communication 107 fmt.Fprintln(os.Stderr, message) 108 } 109 110 func (ui *HeadlessUI) ShowInfo(message string) { 111 //stdout is used by communication 112 fmt.Fprintln(os.Stderr, message) 113 } 114 115 func tmpDirName(t *testing.T) string { 116 d, err := ioutil.TempDir("", "eth-keystore-test") 117 if err != nil { 118 t.Fatal(err) 119 } 120 d, err = filepath.EvalSymlinks(d) 121 if err != nil { 122 t.Fatal(err) 123 } 124 return d 125 } 126 127 func setup(t *testing.T) (*SignerAPI, chan string) { 128 129 controller := make(chan string, 20) 130 131 db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json") 132 if err != nil { 133 utils.Fatalf(err.Error()) 134 } 135 var ( 136 ui = &HeadlessUI{controller} 137 api = NewSignerAPI( 138 1, 139 tmpDirName(t), 140 true, 141 ui, 142 db, 143 true, true) 144 ) 145 return api, controller 146 } 147 func createAccount(control chan string, api *SignerAPI, t *testing.T) { 148 149 control <- "Y" 150 control <- "a_long_password" 151 _, err := api.New(context.Background()) 152 if err != nil { 153 t.Fatal(err) 154 } 155 // Some time to allow changes to propagate 156 time.Sleep(250 * time.Millisecond) 157 } 158 159 func failCreateAccountWithPassword(control chan string, api *SignerAPI, password string, t *testing.T) { 160 161 control <- "Y" 162 control <- password 163 control <- "Y" 164 control <- password 165 control <- "Y" 166 control <- password 167 168 acc, err := api.New(context.Background()) 169 if err == nil { 170 t.Fatal("Should have returned an error") 171 } 172 if acc.Address != (common.Address{}) { 173 t.Fatal("Empty address should be returned") 174 } 175 } 176 177 func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) { 178 control <- "N" 179 acc, err := api.New(context.Background()) 180 if err != ErrRequestDenied { 181 t.Fatal(err) 182 } 183 if acc.Address != (common.Address{}) { 184 t.Fatal("Empty address should be returned") 185 } 186 } 187 188 func list(control chan string, api *SignerAPI, t *testing.T) []common.Address { 189 control <- "A" 190 list, err := api.List(context.Background()) 191 if err != nil { 192 t.Fatal(err) 193 } 194 return list 195 } 196 197 func TestNewAcc(t *testing.T) { 198 api, control := setup(t) 199 verifyNum := func(num int) { 200 if list := list(control, api, t); len(list) != num { 201 t.Errorf("Expected %d accounts, got %d", num, len(list)) 202 } 203 } 204 // Testing create and create-deny 205 createAccount(control, api, t) 206 createAccount(control, api, t) 207 failCreateAccount(control, api, t) 208 failCreateAccount(control, api, t) 209 createAccount(control, api, t) 210 failCreateAccount(control, api, t) 211 createAccount(control, api, t) 212 failCreateAccount(control, api, t) 213 214 verifyNum(4) 215 216 // Fail to create this, due to bad password 217 failCreateAccountWithPassword(control, api, "short", t) 218 failCreateAccountWithPassword(control, api, "longerbutbad\rfoo", t) 219 220 verifyNum(4) 221 222 // Testing listing: 223 // Listing one Account 224 control <- "1" 225 list, err := api.List(context.Background()) 226 if err != nil { 227 t.Fatal(err) 228 } 229 if len(list) != 1 { 230 t.Fatalf("List should only show one Account") 231 } 232 // Listing denied 233 control <- "Nope" 234 list, err = api.List(context.Background()) 235 if len(list) != 0 { 236 t.Fatalf("List should be empty") 237 } 238 if err != ErrRequestDenied { 239 t.Fatal("Expected deny") 240 } 241 } 242 243 func TestSignData(t *testing.T) { 244 api, control := setup(t) 245 //Create two accounts 246 createAccount(control, api, t) 247 createAccount(control, api, t) 248 control <- "1" 249 list, err := api.List(context.Background()) 250 if err != nil { 251 t.Fatal(err) 252 } 253 a := common.NewMixedcaseAddress(list[0]) 254 255 control <- "Y" 256 control <- "wrongpassword" 257 h, err := api.Sign(context.Background(), a, []byte("EHLO world")) 258 if h != nil { 259 t.Errorf("Expected nil-data, got %x", h) 260 } 261 if err != keystore.ErrDecrypt { 262 t.Errorf("Expected ErrLocked! %v", err) 263 } 264 control <- "No way" 265 h, err = api.Sign(context.Background(), a, []byte("EHLO world")) 266 if h != nil { 267 t.Errorf("Expected nil-data, got %x", h) 268 } 269 if err != ErrRequestDenied { 270 t.Errorf("Expected ErrRequestDenied! %v", err) 271 } 272 control <- "Y" 273 control <- "a_long_password" 274 h, err = api.Sign(context.Background(), a, []byte("EHLO world")) 275 if err != nil { 276 t.Fatal(err) 277 } 278 if h == nil || len(h) != 65 { 279 t.Errorf("Expected 65 byte signature (got %d bytes)", len(h)) 280 } 281 } 282 func mkTestTx(from common.MixedcaseAddress) SendTxArgs { 283 to := common.NewMixedcaseAddress(common.HexToAddress("0x1337")) 284 gas := hexutil.Uint64(21000) 285 gasPrice := (hexutil.Big)(*big.NewInt(2000000000)) 286 value := (hexutil.Big)(*big.NewInt(1e18)) 287 nonce := (hexutil.Uint64)(0) 288 data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a")) 289 tx := SendTxArgs{ 290 From: from, 291 To: &to, 292 Gas: gas, 293 GasPrice: gasPrice, 294 Value: value, 295 Data: &data, 296 Nonce: nonce} 297 return tx 298 } 299 300 func TestSignTx(t *testing.T) { 301 var ( 302 list []common.Address 303 res, res2 *ethapi.SignTransactionResult 304 err error 305 ) 306 307 api, control := setup(t) 308 createAccount(control, api, t) 309 control <- "A" 310 list, err = api.List(context.Background()) 311 if err != nil { 312 t.Fatal(err) 313 } 314 a := common.NewMixedcaseAddress(list[0]) 315 316 methodSig := "test(uint)" 317 tx := mkTestTx(a) 318 319 control <- "Y" 320 control <- "wrongpassword" 321 res, err = api.SignTransaction(context.Background(), tx, &methodSig) 322 if res != nil { 323 t.Errorf("Expected nil-response, got %v", res) 324 } 325 if err != keystore.ErrDecrypt { 326 t.Errorf("Expected ErrLocked! %v", err) 327 } 328 control <- "No way" 329 res, err = api.SignTransaction(context.Background(), tx, &methodSig) 330 if res != nil { 331 t.Errorf("Expected nil-response, got %v", res) 332 } 333 if err != ErrRequestDenied { 334 t.Errorf("Expected ErrRequestDenied! %v", err) 335 } 336 control <- "Y" 337 control <- "a_long_password" 338 res, err = api.SignTransaction(context.Background(), tx, &methodSig) 339 340 if err != nil { 341 t.Fatal(err) 342 } 343 parsedTx := &types.Transaction{} 344 rlp.Decode(bytes.NewReader(res.Raw), parsedTx) 345 346 //The tx should NOT be modified by the UI 347 if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 { 348 t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value()) 349 } 350 control <- "Y" 351 control <- "a_long_password" 352 353 res2, err = api.SignTransaction(context.Background(), tx, &methodSig) 354 if err != nil { 355 t.Fatal(err) 356 } 357 if !bytes.Equal(res.Raw, res2.Raw) { 358 t.Error("Expected tx to be unmodified by UI") 359 } 360 361 //The tx is modified by the UI 362 control <- "M" 363 control <- "a_long_password" 364 365 res2, err = api.SignTransaction(context.Background(), tx, &methodSig) 366 if err != nil { 367 t.Fatal(err) 368 } 369 parsedTx2 := &types.Transaction{} 370 rlp.Decode(bytes.NewReader(res.Raw), parsedTx2) 371 372 //The tx should be modified by the UI 373 if parsedTx2.Value().Cmp(tx.Value.ToInt()) != 0 { 374 t.Errorf("Expected value to be unchanged, got %v", parsedTx.Value()) 375 } 376 if bytes.Equal(res.Raw, res2.Raw) { 377 t.Error("Expected tx to be modified by UI") 378 } 379 380 } 381 382 /* 383 func TestAsyncronousResponses(t *testing.T){ 384 385 //Set up one account 386 api, control := setup(t) 387 createAccount(control, api, t) 388 389 // Two transactions, the second one with larger value than the first 390 tx1 := mkTestTx() 391 newVal := big.NewInt(0).Add((*big.Int) (tx1.Value), big.NewInt(1)) 392 tx2 := mkTestTx() 393 tx2.Value = (*hexutil.Big)(newVal) 394 395 control <- "W" //wait 396 control <- "Y" // 397 control <- "a_long_password" 398 control <- "Y" // 399 control <- "a_long_password" 400 401 var err error 402 403 h1, err := api.SignTransaction(context.Background(), common.HexToAddress("1111"), tx1, nil) 404 h2, err := api.SignTransaction(context.Background(), common.HexToAddress("2222"), tx2, nil) 405 406 407 } 408 */