github.com/halybang/go-ethereum@v1.0.5-0.20180325041310-3b262bc1367c/accounts/keystore/keystore_test.go (about) 1 // Copyright 2018 Wanchain Foundation Ltd 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser 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-ethereum 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 Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 18 package keystore 19 20 import ( 21 "io/ioutil" 22 "math/rand" 23 "os" 24 "runtime" 25 "sort" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/wanchain/go-wanchain/accounts" 31 "github.com/wanchain/go-wanchain/common" 32 "github.com/wanchain/go-wanchain/common/hexutil" 33 "github.com/wanchain/go-wanchain/crypto" 34 "github.com/wanchain/go-wanchain/event" 35 ) 36 37 var testSigData = make([]byte, 32) 38 39 func TestKeyStore(t *testing.T) { 40 dir, ks := tmpKeyStore(t, true) 41 defer os.RemoveAll(dir) 42 43 // create an account 44 auth := "wanchain_test" 45 a, err := ks.NewAccount(auth) 46 if err != nil { 47 t.Fatal(err) 48 } 49 if !strings.HasPrefix(a.URL.Path, dir) { 50 t.Errorf("account file %s doesn't have dir prefix", a.URL) 51 } 52 stat, err := os.Stat(a.URL.Path) 53 if err != nil { 54 t.Fatalf("account file %s doesn't exist (%v)", a.URL, err) 55 } 56 if runtime.GOOS != "windows" && stat.Mode() != 0600 { 57 t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600) 58 } 59 if !ks.HasAddress(a.Address) { 60 t.Errorf("HasAccount(%x) should've returned true", a.Address) 61 } 62 if err := ks.Unlock(a, auth); err != nil { 63 t.Errorf("Unlock error: %v", err) 64 } 65 var wAddr common.WAddress 66 wAddr, err = ks.GetWanAddress(a) 67 if err != nil && len(wAddr) != common.WAddressLength { 68 t.Errorf("Generate waddress error: %v", err) 69 } 70 if _, err := genOTA(hexutil.Encode(wAddr[:])); err != nil { 71 t.Errorf("Generate OTA error: %v", err) 72 } 73 if err := ks.Update(a, auth, auth+"_new"); err != nil { 74 t.Errorf("Update error: %v", err) 75 } 76 if err := ks.Delete(a, auth+"_new"); err != nil { 77 t.Errorf("Delete error: %v", err) 78 } 79 if common.FileExist(a.URL.Path) { 80 t.Errorf("account file %s should be gone after Delete", a.URL) 81 } 82 if ks.HasAddress(a.Address) { 83 t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address) 84 } 85 } 86 87 // func TestImportExportECDSAPair(t *testing.T) { 88 // // create a keystore wo work with 89 // dir, ks := tmpKeyStore(t, true) 90 // defer os.RemoveAll(dir) 91 92 // // create an account 93 // auth := "wanchain_test" 94 // a, err := ks.NewAccount(auth) 95 // if err != nil { 96 // t.Fatal(err) 97 // } 98 99 // if !strings.HasPrefix(a.URL.Path, dir) { 100 // t.Errorf("account file %s doesn't have dir prefix", a.URL) 101 // } 102 // stat, err := os.Stat(a.URL.Path) 103 // if err != nil { 104 // t.Fatalf("account file %s doesn't exist (%v)", a.URL, err) 105 // } 106 // if runtime.GOOS != "windows" && stat.Mode() != 0600 { 107 // t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600) 108 // } 109 // if !ks.HasAddress(a.Address) { 110 // t.Errorf("HasAccount(%x) should've returned true", a.Address) 111 // } 112 // if err := ks.Unlock(a, auth); err != nil { 113 // t.Errorf("Unlock error: %v", err) 114 // } 115 // fp, err := os.Create(dir + "/" + "ecdsa_key_pair_out") 116 // if err != nil { 117 // t.Fatal(err) 118 // } 119 // fn := fp.Name() 120 // if !strings.HasPrefix(fn, dir) { 121 // t.Fatalf("output file %s doesn't have dir prefix", fn) 122 // } 123 124 // r, r1, err := ks.ExportECDSA(a, auth) 125 // if err != nil { 126 // t.Fatal(err) 127 // } 128 129 // err = ExportECDSAPair(hex.EncodeToString(r), hex.EncodeToString(r1), fn) 130 // if err != nil { 131 // t.Fatal(err) 132 // } 133 134 // s, s1, err := LoadECDSAPair(fn) 135 // if err != nil { 136 // t.Fatal(err) 137 // } 138 139 // _, key, err := ks.getDecryptedKey(a, auth) 140 141 // if s.D.Cmp(key.PrivateKey.D) != 0 && s1.D.Cmp(key.PrivateKey2.D) != 0 { 142 // t.Fatal("Import ecdsa key pair error!") 143 // } 144 // } 145 146 func TestSign(t *testing.T) { 147 dir, ks := tmpKeyStore(t, true) 148 defer os.RemoveAll(dir) 149 150 pass := "" // not used but required by API 151 a1, err := ks.NewAccount(pass) 152 if err != nil { 153 t.Fatal(err) 154 } 155 if err := ks.Unlock(a1, ""); err != nil { 156 t.Fatal(err) 157 } 158 if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil { 159 t.Fatal(err) 160 } 161 } 162 163 func TestSignWithPassphrase(t *testing.T) { 164 dir, ks := tmpKeyStore(t, true) 165 defer os.RemoveAll(dir) 166 167 pass := "passwd" 168 acc, err := ks.NewAccount(pass) 169 if err != nil { 170 t.Fatal(err) 171 } 172 173 if _, unlocked := ks.unlocked[acc.Address]; unlocked { 174 t.Fatal("expected account to be locked") 175 } 176 177 _, err = ks.SignHashWithPassphrase(acc, pass, testSigData) 178 if err != nil { 179 t.Fatal(err) 180 } 181 182 if _, unlocked := ks.unlocked[acc.Address]; unlocked { 183 t.Fatal("expected account to be locked") 184 } 185 186 if _, err = ks.SignHashWithPassphrase(acc, "invalid passwd", testSigData); err == nil { 187 t.Fatal("expected SignHashWithPassphrase to fail with invalid password") 188 } 189 } 190 191 func TestTimedUnlock(t *testing.T) { 192 dir, ks := tmpKeyStore(t, true) 193 defer os.RemoveAll(dir) 194 195 pass := "foo" 196 a1, err := ks.NewAccount(pass) 197 if err != nil { 198 t.Fatal(err) 199 } 200 201 // Signing without passphrase fails because account is locked 202 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 203 if err != ErrLocked { 204 t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err) 205 } 206 207 // Signing with passphrase works 208 if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { 209 t.Fatal(err) 210 } 211 212 // Signing without passphrase works because account is temp unlocked 213 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 214 if err != nil { 215 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 216 } 217 218 // Signing fails again after automatic locking 219 time.Sleep(250 * time.Millisecond) 220 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 221 if err != ErrLocked { 222 t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) 223 } 224 } 225 226 func TestOverrideUnlock(t *testing.T) { 227 dir, ks := tmpKeyStore(t, false) 228 defer os.RemoveAll(dir) 229 230 pass := "foo" 231 a1, err := ks.NewAccount(pass) 232 if err != nil { 233 t.Fatal(err) 234 } 235 236 // Unlock indefinitely. 237 if err = ks.TimedUnlock(a1, pass, 5*time.Minute); err != nil { 238 t.Fatal(err) 239 } 240 241 // Signing without passphrase works because account is temp unlocked 242 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 243 if err != nil { 244 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 245 } 246 247 // reset unlock to a shorter period, invalidates the previous unlock 248 if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { 249 t.Fatal(err) 250 } 251 252 // Signing without passphrase still works because account is temp unlocked 253 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 254 if err != nil { 255 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 256 } 257 258 // Signing fails again after automatic locking 259 time.Sleep(250 * time.Millisecond) 260 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 261 if err != ErrLocked { 262 t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) 263 } 264 } 265 266 // This test should fail under -race if signing races the expiration goroutine. 267 func TestSignRace(t *testing.T) { 268 dir, ks := tmpKeyStore(t, false) 269 defer os.RemoveAll(dir) 270 271 // Create a test account. 272 a1, err := ks.NewAccount("") 273 if err != nil { 274 t.Fatal("could not create the test account", err) 275 } 276 277 if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil { 278 t.Fatal("could not unlock the test account", err) 279 } 280 end := time.Now().Add(500 * time.Millisecond) 281 for time.Now().Before(end) { 282 if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked { 283 return 284 } else if err != nil { 285 t.Errorf("Sign error: %v", err) 286 return 287 } 288 time.Sleep(1 * time.Millisecond) 289 } 290 t.Errorf("Account did not lock within the timeout") 291 } 292 293 // Tests that the wallet notifier loop starts and stops correctly based on the 294 // addition and removal of wallet event subscriptions. 295 func TestWalletNotifierLifecycle(t *testing.T) { 296 // Create a temporary kesytore to test with 297 dir, ks := tmpKeyStore(t, false) 298 defer os.RemoveAll(dir) 299 300 // Ensure that the notification updater is not running yet 301 time.Sleep(250 * time.Millisecond) 302 ks.mu.RLock() 303 updating := ks.updating 304 ks.mu.RUnlock() 305 306 if updating { 307 t.Errorf("wallet notifier running without subscribers") 308 } 309 // Subscribe to the wallet feed and ensure the updater boots up 310 updates := make(chan accounts.WalletEvent) 311 312 subs := make([]event.Subscription, 2) 313 for i := 0; i < len(subs); i++ { 314 // Create a new subscription 315 subs[i] = ks.Subscribe(updates) 316 317 // Ensure the notifier comes online 318 time.Sleep(250 * time.Millisecond) 319 ks.mu.RLock() 320 updating = ks.updating 321 ks.mu.RUnlock() 322 323 if !updating { 324 t.Errorf("sub %d: wallet notifier not running after subscription", i) 325 } 326 } 327 // Unsubscribe and ensure the updater terminates eventually 328 for i := 0; i < len(subs); i++ { 329 // Close an existing subscription 330 subs[i].Unsubscribe() 331 332 // Ensure the notifier shuts down at and only at the last close 333 for k := 0; k < int(walletRefreshCycle/(250*time.Millisecond))+2; k++ { 334 ks.mu.RLock() 335 updating = ks.updating 336 ks.mu.RUnlock() 337 338 if i < len(subs)-1 && !updating { 339 t.Fatalf("sub %d: event notifier stopped prematurely", i) 340 } 341 if i == len(subs)-1 && !updating { 342 return 343 } 344 time.Sleep(250 * time.Millisecond) 345 } 346 } 347 t.Errorf("wallet notifier didn't terminate after unsubscribe") 348 } 349 350 type walletEvent struct { 351 accounts.WalletEvent 352 a accounts.Account 353 } 354 355 // Tests that wallet notifications and correctly fired when accounts are added 356 // or deleted from the keystore. 357 func TestWalletNotifications(t *testing.T) { 358 dir, ks := tmpKeyStore(t, false) 359 defer os.RemoveAll(dir) 360 361 // Subscribe to the wallet feed and collect events. 362 var ( 363 events []walletEvent 364 updates = make(chan accounts.WalletEvent) 365 sub = ks.Subscribe(updates) 366 ) 367 defer sub.Unsubscribe() 368 go func() { 369 for { 370 select { 371 case ev := <-updates: 372 events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]}) 373 case <-sub.Err(): 374 close(updates) 375 return 376 } 377 } 378 }() 379 380 // Randomly add and remove accounts. 381 var ( 382 live = make(map[common.Address]accounts.Account) 383 wantEvents []walletEvent 384 ) 385 for i := 0; i < 1024; i++ { 386 if create := len(live) == 0 || rand.Int()%4 > 0; create { 387 // Add a new account and ensure wallet notifications arrives 388 account, err := ks.NewAccount("") 389 if err != nil { 390 t.Fatalf("failed to create test account: %v", err) 391 } 392 live[account.Address] = account 393 wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account}) 394 } else { 395 // Delete a random account. 396 var account accounts.Account 397 for _, a := range live { 398 account = a 399 break 400 } 401 if err := ks.Delete(account, ""); err != nil { 402 t.Fatalf("failed to delete test account: %v", err) 403 } 404 delete(live, account.Address) 405 wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account}) 406 } 407 } 408 409 // Shut down the event collector and check events. 410 sub.Unsubscribe() 411 <-updates 412 checkAccounts(t, live, ks.Wallets()) 413 checkEvents(t, wantEvents, events) 414 } 415 416 // checkAccounts checks that all known live accounts are present in the wallet list. 417 func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) { 418 if len(live) != len(wallets) { 419 t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live)) 420 return 421 } 422 liveList := make([]accounts.Account, 0, len(live)) 423 for _, account := range live { 424 liveList = append(liveList, account) 425 } 426 sort.Sort(accountsByURL(liveList)) 427 for j, wallet := range wallets { 428 if accs := wallet.Accounts(); len(accs) != 1 { 429 t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs)) 430 } else if accs[0] != liveList[j] { 431 t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j]) 432 } 433 } 434 } 435 436 // checkEvents checks that all events in 'want' are present in 'have'. Events may be present multiple times. 437 func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) { 438 for _, wantEv := range want { 439 nmatch := 0 440 for ; len(have) > 0; nmatch++ { 441 if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a { 442 break 443 } 444 have = have[1:] 445 } 446 if nmatch == 0 { 447 t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address) 448 } 449 } 450 } 451 452 func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) { 453 d, err := ioutil.TempDir("", "wanchain-keystore-test") 454 if err != nil { 455 t.Fatal(err) 456 } 457 new := NewPlaintextKeyStore 458 if encrypted { 459 new = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) } 460 } 461 return d, new(d) 462 } 463 464 func genOTA(wanStr string) (string, error) { 465 wanRaw, err := hexutil.Decode(wanStr) 466 if err != nil { 467 return "", err 468 } 469 470 PK1, PK2, err := GeneratePKPairFromWAddress(wanRaw) 471 if err != nil { 472 return "", err 473 } 474 475 PKPairStr := hexutil.PKPair2HexSlice(PK1, PK2) 476 SKOTA, err := crypto.GenerateOneTimeKey(PKPairStr[0], PKPairStr[1], PKPairStr[2], PKPairStr[3]) 477 if err != nil { 478 return "", err 479 } 480 481 otaStr := strings.Replace(strings.Join(SKOTA, ""), "0x", "", -1) 482 raw, err := hexutil.Decode("0x" + otaStr) 483 if err != nil { 484 return "", err 485 } 486 487 rawWanAddr, err := WaddrFromUncompressedRawBytes(raw) 488 if err != nil || rawWanAddr == nil { 489 return "", nil 490 } 491 492 return hexutil.Encode(rawWanAddr[:]), nil 493 } 494 495 func TestComputeOTAPPKeys(t *testing.T) { 496 dir, ks := tmpKeyStore(t, true) 497 defer os.RemoveAll(dir) 498 499 // create an account 500 auth := "wanchain_test" 501 a, err := ks.NewAccount(auth) 502 if err != nil { 503 t.Fatal(err) 504 } 505 506 err = ks.Unlock(a, auth) 507 if err != nil { 508 t.Errorf("unlock fail. err:%s", err.Error()) 509 } 510 511 var wAddr common.WAddress 512 wAddr, err = ks.GetWanAddress(a) 513 if err != nil && len(wAddr) != common.WAddressLength { 514 t.Errorf("Generate waddress error: %v", err) 515 } 516 517 PK1, PK2, err := GeneratePKPairFromWAddress(wAddr[:]) 518 if err != nil { 519 t.Errorf("generate PK pair from wan address fail. err:%s", err.Error()) 520 } 521 522 PKPairStr := hexutil.PKPair2HexSlice(PK1, PK2) 523 SKOTA, err := crypto.GenerateOneTimeKey(PKPairStr[0], PKPairStr[1], PKPairStr[2], PKPairStr[3]) 524 if err != nil { 525 t.Errorf("generate one time key fail. err:%s", err.Error()) 526 } 527 528 pk, err := ks.ComputeOTAPPKeys(a, SKOTA[0], SKOTA[1], SKOTA[2], SKOTA[3]) 529 if err != nil { 530 t.Errorf("compute ota ppkey fail. err:%s", err.Error()) 531 } 532 533 if len(pk) != 4 || len(pk[0]) == 0 || len(pk[1]) == 0 || len(pk[2]) == 0 { 534 t.Errorf("invalid ota pk. pk lenght:%d", len(pk)) 535 } 536 }