github.com/zhiqiangxu/go-ethereum@v1.9.16-0.20210824055606-be91cfdebc48/accounts/keystore/keystore_test.go (about) 1 // Copyright 2017 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 keystore 18 19 import ( 20 "io/ioutil" 21 "math/rand" 22 "os" 23 "runtime" 24 "sort" 25 "strings" 26 "sync" 27 "sync/atomic" 28 "testing" 29 "time" 30 31 "github.com/zhiqiangxu/go-ethereum/accounts" 32 "github.com/zhiqiangxu/go-ethereum/common" 33 "github.com/zhiqiangxu/go-ethereum/crypto" 34 "github.com/zhiqiangxu/go-ethereum/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 a, err := ks.NewAccount("foo") 44 if err != nil { 45 t.Fatal(err) 46 } 47 if !strings.HasPrefix(a.URL.Path, dir) { 48 t.Errorf("account file %s doesn't have dir prefix", a.URL) 49 } 50 stat, err := os.Stat(a.URL.Path) 51 if err != nil { 52 t.Fatalf("account file %s doesn't exist (%v)", a.URL, err) 53 } 54 if runtime.GOOS != "windows" && stat.Mode() != 0600 { 55 t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600) 56 } 57 if !ks.HasAddress(a.Address) { 58 t.Errorf("HasAccount(%x) should've returned true", a.Address) 59 } 60 if err := ks.Update(a, "foo", "bar"); err != nil { 61 t.Errorf("Update error: %v", err) 62 } 63 if err := ks.Delete(a, "bar"); err != nil { 64 t.Errorf("Delete error: %v", err) 65 } 66 if common.FileExist(a.URL.Path) { 67 t.Errorf("account file %s should be gone after Delete", a.URL) 68 } 69 if ks.HasAddress(a.Address) { 70 t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address) 71 } 72 } 73 74 func TestSign(t *testing.T) { 75 dir, ks := tmpKeyStore(t, true) 76 defer os.RemoveAll(dir) 77 78 pass := "" // not used but required by API 79 a1, err := ks.NewAccount(pass) 80 if err != nil { 81 t.Fatal(err) 82 } 83 if err := ks.Unlock(a1, ""); err != nil { 84 t.Fatal(err) 85 } 86 if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil { 87 t.Fatal(err) 88 } 89 } 90 91 func TestSignWithPassphrase(t *testing.T) { 92 dir, ks := tmpKeyStore(t, true) 93 defer os.RemoveAll(dir) 94 95 pass := "passwd" 96 acc, err := ks.NewAccount(pass) 97 if err != nil { 98 t.Fatal(err) 99 } 100 101 if _, unlocked := ks.unlocked[acc.Address]; unlocked { 102 t.Fatal("expected account to be locked") 103 } 104 105 _, err = ks.SignHashWithPassphrase(acc, pass, testSigData) 106 if err != nil { 107 t.Fatal(err) 108 } 109 110 if _, unlocked := ks.unlocked[acc.Address]; unlocked { 111 t.Fatal("expected account to be locked") 112 } 113 114 if _, err = ks.SignHashWithPassphrase(acc, "invalid passwd", testSigData); err == nil { 115 t.Fatal("expected SignHashWithPassphrase to fail with invalid password") 116 } 117 } 118 119 func TestTimedUnlock(t *testing.T) { 120 dir, ks := tmpKeyStore(t, true) 121 defer os.RemoveAll(dir) 122 123 pass := "foo" 124 a1, err := ks.NewAccount(pass) 125 if err != nil { 126 t.Fatal(err) 127 } 128 129 // Signing without passphrase fails because account is locked 130 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 131 if err != ErrLocked { 132 t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err) 133 } 134 135 // Signing with passphrase works 136 if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { 137 t.Fatal(err) 138 } 139 140 // Signing without passphrase works because account is temp unlocked 141 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 142 if err != nil { 143 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 144 } 145 146 // Signing fails again after automatic locking 147 time.Sleep(250 * time.Millisecond) 148 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 149 if err != ErrLocked { 150 t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) 151 } 152 } 153 154 func TestOverrideUnlock(t *testing.T) { 155 dir, ks := tmpKeyStore(t, false) 156 defer os.RemoveAll(dir) 157 158 pass := "foo" 159 a1, err := ks.NewAccount(pass) 160 if err != nil { 161 t.Fatal(err) 162 } 163 164 // Unlock indefinitely. 165 if err = ks.TimedUnlock(a1, pass, 5*time.Minute); err != nil { 166 t.Fatal(err) 167 } 168 169 // Signing without passphrase works because account is temp unlocked 170 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 171 if err != nil { 172 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 173 } 174 175 // reset unlock to a shorter period, invalidates the previous unlock 176 if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { 177 t.Fatal(err) 178 } 179 180 // Signing without passphrase still works because account is temp unlocked 181 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 182 if err != nil { 183 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 184 } 185 186 // Signing fails again after automatic locking 187 time.Sleep(250 * time.Millisecond) 188 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 189 if err != ErrLocked { 190 t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) 191 } 192 } 193 194 // This test should fail under -race if signing races the expiration goroutine. 195 func TestSignRace(t *testing.T) { 196 dir, ks := tmpKeyStore(t, false) 197 defer os.RemoveAll(dir) 198 199 // Create a test account. 200 a1, err := ks.NewAccount("") 201 if err != nil { 202 t.Fatal("could not create the test account", err) 203 } 204 205 if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil { 206 t.Fatal("could not unlock the test account", err) 207 } 208 end := time.Now().Add(500 * time.Millisecond) 209 for time.Now().Before(end) { 210 if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked { 211 return 212 } else if err != nil { 213 t.Errorf("Sign error: %v", err) 214 return 215 } 216 time.Sleep(1 * time.Millisecond) 217 } 218 t.Errorf("Account did not lock within the timeout") 219 } 220 221 // Tests that the wallet notifier loop starts and stops correctly based on the 222 // addition and removal of wallet event subscriptions. 223 func TestWalletNotifierLifecycle(t *testing.T) { 224 // Create a temporary kesytore to test with 225 dir, ks := tmpKeyStore(t, false) 226 defer os.RemoveAll(dir) 227 228 // Ensure that the notification updater is not running yet 229 time.Sleep(250 * time.Millisecond) 230 ks.mu.RLock() 231 updating := ks.updating 232 ks.mu.RUnlock() 233 234 if updating { 235 t.Errorf("wallet notifier running without subscribers") 236 } 237 // Subscribe to the wallet feed and ensure the updater boots up 238 updates := make(chan accounts.WalletEvent) 239 240 subs := make([]event.Subscription, 2) 241 for i := 0; i < len(subs); i++ { 242 // Create a new subscription 243 subs[i] = ks.Subscribe(updates) 244 245 // Ensure the notifier comes online 246 time.Sleep(250 * time.Millisecond) 247 ks.mu.RLock() 248 updating = ks.updating 249 ks.mu.RUnlock() 250 251 if !updating { 252 t.Errorf("sub %d: wallet notifier not running after subscription", i) 253 } 254 } 255 // Unsubscribe and ensure the updater terminates eventually 256 for i := 0; i < len(subs); i++ { 257 // Close an existing subscription 258 subs[i].Unsubscribe() 259 260 // Ensure the notifier shuts down at and only at the last close 261 for k := 0; k < int(walletRefreshCycle/(250*time.Millisecond))+2; k++ { 262 ks.mu.RLock() 263 updating = ks.updating 264 ks.mu.RUnlock() 265 266 if i < len(subs)-1 && !updating { 267 t.Fatalf("sub %d: event notifier stopped prematurely", i) 268 } 269 if i == len(subs)-1 && !updating { 270 return 271 } 272 time.Sleep(250 * time.Millisecond) 273 } 274 } 275 t.Errorf("wallet notifier didn't terminate after unsubscribe") 276 } 277 278 type walletEvent struct { 279 accounts.WalletEvent 280 a accounts.Account 281 } 282 283 // Tests that wallet notifications and correctly fired when accounts are added 284 // or deleted from the keystore. 285 func TestWalletNotifications(t *testing.T) { 286 dir, ks := tmpKeyStore(t, false) 287 defer os.RemoveAll(dir) 288 289 // Subscribe to the wallet feed and collect events. 290 var ( 291 events []walletEvent 292 updates = make(chan accounts.WalletEvent) 293 sub = ks.Subscribe(updates) 294 ) 295 defer sub.Unsubscribe() 296 go func() { 297 for { 298 select { 299 case ev := <-updates: 300 events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]}) 301 case <-sub.Err(): 302 close(updates) 303 return 304 } 305 } 306 }() 307 308 // Randomly add and remove accounts. 309 var ( 310 live = make(map[common.Address]accounts.Account) 311 wantEvents []walletEvent 312 ) 313 for i := 0; i < 1024; i++ { 314 if create := len(live) == 0 || rand.Int()%4 > 0; create { 315 // Add a new account and ensure wallet notifications arrives 316 account, err := ks.NewAccount("") 317 if err != nil { 318 t.Fatalf("failed to create test account: %v", err) 319 } 320 live[account.Address] = account 321 wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account}) 322 } else { 323 // Delete a random account. 324 var account accounts.Account 325 for _, a := range live { 326 account = a 327 break 328 } 329 if err := ks.Delete(account, ""); err != nil { 330 t.Fatalf("failed to delete test account: %v", err) 331 } 332 delete(live, account.Address) 333 wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account}) 334 } 335 } 336 337 // Shut down the event collector and check events. 338 sub.Unsubscribe() 339 <-updates 340 checkAccounts(t, live, ks.Wallets()) 341 checkEvents(t, wantEvents, events) 342 } 343 344 // TestImportExport tests the import functionality of a keystore. 345 func TestImportECDSA(t *testing.T) { 346 dir, ks := tmpKeyStore(t, true) 347 defer os.RemoveAll(dir) 348 key, err := crypto.GenerateKey() 349 if err != nil { 350 t.Fatalf("failed to generate key: %v", key) 351 } 352 if _, err = ks.ImportECDSA(key, "old"); err != nil { 353 t.Errorf("importing failed: %v", err) 354 } 355 if _, err = ks.ImportECDSA(key, "old"); err == nil { 356 t.Errorf("importing same key twice succeeded") 357 } 358 if _, err = ks.ImportECDSA(key, "new"); err == nil { 359 t.Errorf("importing same key twice succeeded") 360 } 361 } 362 363 // TestImportECDSA tests the import and export functionality of a keystore. 364 func TestImportExport(t *testing.T) { 365 dir, ks := tmpKeyStore(t, true) 366 defer os.RemoveAll(dir) 367 acc, err := ks.NewAccount("old") 368 if err != nil { 369 t.Fatalf("failed to create account: %v", acc) 370 } 371 json, err := ks.Export(acc, "old", "new") 372 if err != nil { 373 t.Fatalf("failed to export account: %v", acc) 374 } 375 dir2, ks2 := tmpKeyStore(t, true) 376 defer os.RemoveAll(dir2) 377 if _, err = ks2.Import(json, "old", "old"); err == nil { 378 t.Errorf("importing with invalid password succeeded") 379 } 380 acc2, err := ks2.Import(json, "new", "new") 381 if err != nil { 382 t.Errorf("importing failed: %v", err) 383 } 384 if acc.Address != acc2.Address { 385 t.Error("imported account does not match exported account") 386 } 387 if _, err = ks2.Import(json, "new", "new"); err == nil { 388 t.Errorf("importing a key twice succeeded") 389 } 390 391 } 392 393 // TestImportRace tests the keystore on races. 394 // This test should fail under -race if importing races. 395 func TestImportRace(t *testing.T) { 396 dir, ks := tmpKeyStore(t, true) 397 defer os.RemoveAll(dir) 398 acc, err := ks.NewAccount("old") 399 if err != nil { 400 t.Fatalf("failed to create account: %v", acc) 401 } 402 json, err := ks.Export(acc, "old", "new") 403 if err != nil { 404 t.Fatalf("failed to export account: %v", acc) 405 } 406 dir2, ks2 := tmpKeyStore(t, true) 407 defer os.RemoveAll(dir2) 408 var atom uint32 409 var wg sync.WaitGroup 410 wg.Add(2) 411 for i := 0; i < 2; i++ { 412 go func() { 413 defer wg.Done() 414 if _, err := ks2.Import(json, "new", "new"); err != nil { 415 atomic.AddUint32(&atom, 1) 416 } 417 418 }() 419 } 420 wg.Wait() 421 if atom != 1 { 422 t.Errorf("Import is racy") 423 } 424 } 425 426 // checkAccounts checks that all known live accounts are present in the wallet list. 427 func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) { 428 if len(live) != len(wallets) { 429 t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live)) 430 return 431 } 432 liveList := make([]accounts.Account, 0, len(live)) 433 for _, account := range live { 434 liveList = append(liveList, account) 435 } 436 sort.Sort(accountsByURL(liveList)) 437 for j, wallet := range wallets { 438 if accs := wallet.Accounts(); len(accs) != 1 { 439 t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs)) 440 } else if accs[0] != liveList[j] { 441 t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j]) 442 } 443 } 444 } 445 446 // checkEvents checks that all events in 'want' are present in 'have'. Events may be present multiple times. 447 func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) { 448 for _, wantEv := range want { 449 nmatch := 0 450 for ; len(have) > 0; nmatch++ { 451 if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a { 452 break 453 } 454 have = have[1:] 455 } 456 if nmatch == 0 { 457 t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address) 458 } 459 } 460 } 461 462 func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) { 463 d, err := ioutil.TempDir("", "eth-keystore-test") 464 if err != nil { 465 t.Fatal(err) 466 } 467 newKs := NewPlaintextKeyStore 468 if encrypted { 469 newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) } 470 } 471 return d, newKs(d) 472 }