github.com/benorgera/go-ethereum@v1.10.18-0.20220401011646-b3f57b1a73ba/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/ethereum/go-ethereum/accounts" 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/crypto" 34 "github.com/ethereum/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 for ev := range updates { 340 events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]}) 341 } 342 checkAccounts(t, live, ks.Wallets()) 343 checkEvents(t, wantEvents, events) 344 } 345 346 // TestImportExport tests the import functionality of a keystore. 347 func TestImportECDSA(t *testing.T) { 348 dir, ks := tmpKeyStore(t, true) 349 defer os.RemoveAll(dir) 350 key, err := crypto.GenerateKey() 351 if err != nil { 352 t.Fatalf("failed to generate key: %v", key) 353 } 354 if _, err = ks.ImportECDSA(key, "old"); err != nil { 355 t.Errorf("importing failed: %v", err) 356 } 357 if _, err = ks.ImportECDSA(key, "old"); err == nil { 358 t.Errorf("importing same key twice succeeded") 359 } 360 if _, err = ks.ImportECDSA(key, "new"); err == nil { 361 t.Errorf("importing same key twice succeeded") 362 } 363 } 364 365 // TestImportECDSA tests the import and export functionality of a keystore. 366 func TestImportExport(t *testing.T) { 367 dir, ks := tmpKeyStore(t, true) 368 defer os.RemoveAll(dir) 369 acc, err := ks.NewAccount("old") 370 if err != nil { 371 t.Fatalf("failed to create account: %v", acc) 372 } 373 json, err := ks.Export(acc, "old", "new") 374 if err != nil { 375 t.Fatalf("failed to export account: %v", acc) 376 } 377 dir2, ks2 := tmpKeyStore(t, true) 378 defer os.RemoveAll(dir2) 379 if _, err = ks2.Import(json, "old", "old"); err == nil { 380 t.Errorf("importing with invalid password succeeded") 381 } 382 acc2, err := ks2.Import(json, "new", "new") 383 if err != nil { 384 t.Errorf("importing failed: %v", err) 385 } 386 if acc.Address != acc2.Address { 387 t.Error("imported account does not match exported account") 388 } 389 if _, err = ks2.Import(json, "new", "new"); err == nil { 390 t.Errorf("importing a key twice succeeded") 391 } 392 393 } 394 395 // TestImportRace tests the keystore on races. 396 // This test should fail under -race if importing races. 397 func TestImportRace(t *testing.T) { 398 dir, ks := tmpKeyStore(t, true) 399 defer os.RemoveAll(dir) 400 acc, err := ks.NewAccount("old") 401 if err != nil { 402 t.Fatalf("failed to create account: %v", acc) 403 } 404 json, err := ks.Export(acc, "old", "new") 405 if err != nil { 406 t.Fatalf("failed to export account: %v", acc) 407 } 408 dir2, ks2 := tmpKeyStore(t, true) 409 defer os.RemoveAll(dir2) 410 var atom uint32 411 var wg sync.WaitGroup 412 wg.Add(2) 413 for i := 0; i < 2; i++ { 414 go func() { 415 defer wg.Done() 416 if _, err := ks2.Import(json, "new", "new"); err != nil { 417 atomic.AddUint32(&atom, 1) 418 } 419 420 }() 421 } 422 wg.Wait() 423 if atom != 1 { 424 t.Errorf("Import is racy") 425 } 426 } 427 428 // checkAccounts checks that all known live accounts are present in the wallet list. 429 func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) { 430 if len(live) != len(wallets) { 431 t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live)) 432 return 433 } 434 liveList := make([]accounts.Account, 0, len(live)) 435 for _, account := range live { 436 liveList = append(liveList, account) 437 } 438 sort.Sort(accountsByURL(liveList)) 439 for j, wallet := range wallets { 440 if accs := wallet.Accounts(); len(accs) != 1 { 441 t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs)) 442 } else if accs[0] != liveList[j] { 443 t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j]) 444 } 445 } 446 } 447 448 // checkEvents checks that all events in 'want' are present in 'have'. Events may be present multiple times. 449 func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) { 450 for _, wantEv := range want { 451 nmatch := 0 452 for ; len(have) > 0; nmatch++ { 453 if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a { 454 break 455 } 456 have = have[1:] 457 } 458 if nmatch == 0 { 459 t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address) 460 } 461 } 462 } 463 464 func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) { 465 d, err := ioutil.TempDir("", "eth-keystore-test") 466 if err != nil { 467 t.Fatal(err) 468 } 469 newKs := NewPlaintextKeyStore 470 if encrypted { 471 newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) } 472 } 473 return d, newKs(d) 474 }