github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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 "math/rand" 21 "os" 22 "runtime" 23 "slices" 24 "strings" 25 "sync" 26 "sync/atomic" 27 "testing" 28 "time" 29 30 "github.com/ethereum/go-ethereum/accounts" 31 "github.com/ethereum/go-ethereum/common" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/event" 34 ) 35 36 var testSigData = make([]byte, 32) 37 38 func TestKeyStore(t *testing.T) { 39 t.Parallel() 40 dir, ks := tmpKeyStore(t) 41 42 a, err := ks.NewAccount("foo") 43 if err != nil { 44 t.Fatal(err) 45 } 46 if !strings.HasPrefix(a.URL.Path, dir) { 47 t.Errorf("account file %s doesn't have dir prefix", a.URL) 48 } 49 stat, err := os.Stat(a.URL.Path) 50 if err != nil { 51 t.Fatalf("account file %s doesn't exist (%v)", a.URL, err) 52 } 53 if runtime.GOOS != "windows" && stat.Mode() != 0600 { 54 t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600) 55 } 56 if !ks.HasAddress(a.Address) { 57 t.Errorf("HasAccount(%x) should've returned true", a.Address) 58 } 59 if err := ks.Update(a, "foo", "bar"); err != nil { 60 t.Errorf("Update error: %v", err) 61 } 62 if err := ks.Delete(a, "bar"); err != nil { 63 t.Errorf("Delete error: %v", err) 64 } 65 if common.FileExist(a.URL.Path) { 66 t.Errorf("account file %s should be gone after Delete", a.URL) 67 } 68 if ks.HasAddress(a.Address) { 69 t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address) 70 } 71 } 72 73 func TestSign(t *testing.T) { 74 t.Parallel() 75 _, ks := tmpKeyStore(t) 76 77 pass := "" // not used but required by API 78 a1, err := ks.NewAccount(pass) 79 if err != nil { 80 t.Fatal(err) 81 } 82 if err := ks.Unlock(a1, ""); err != nil { 83 t.Fatal(err) 84 } 85 if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil { 86 t.Fatal(err) 87 } 88 } 89 90 func TestSignWithPassphrase(t *testing.T) { 91 t.Parallel() 92 _, ks := tmpKeyStore(t) 93 94 pass := "passwd" 95 acc, err := ks.NewAccount(pass) 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 if _, unlocked := ks.unlocked[acc.Address]; unlocked { 101 t.Fatal("expected account to be locked") 102 } 103 104 _, err = ks.SignHashWithPassphrase(acc, pass, testSigData) 105 if err != nil { 106 t.Fatal(err) 107 } 108 109 if _, unlocked := ks.unlocked[acc.Address]; unlocked { 110 t.Fatal("expected account to be locked") 111 } 112 113 if _, err = ks.SignHashWithPassphrase(acc, "invalid passwd", testSigData); err == nil { 114 t.Fatal("expected SignHashWithPassphrase to fail with invalid password") 115 } 116 } 117 118 func TestTimedUnlock(t *testing.T) { 119 t.Parallel() 120 _, ks := tmpKeyStore(t) 121 122 pass := "foo" 123 a1, err := ks.NewAccount(pass) 124 if err != nil { 125 t.Fatal(err) 126 } 127 128 // Signing without passphrase fails because account is locked 129 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 130 if err != ErrLocked { 131 t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err) 132 } 133 134 // Signing with passphrase works 135 if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { 136 t.Fatal(err) 137 } 138 139 // Signing without passphrase works because account is temp unlocked 140 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 141 if err != nil { 142 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 143 } 144 145 // Signing fails again after automatic locking 146 time.Sleep(250 * time.Millisecond) 147 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 148 if err != ErrLocked { 149 t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) 150 } 151 } 152 153 func TestOverrideUnlock(t *testing.T) { 154 t.Parallel() 155 _, ks := tmpKeyStore(t) 156 157 pass := "foo" 158 a1, err := ks.NewAccount(pass) 159 if err != nil { 160 t.Fatal(err) 161 } 162 163 // Unlock indefinitely. 164 if err = ks.TimedUnlock(a1, pass, 5*time.Minute); err != nil { 165 t.Fatal(err) 166 } 167 168 // Signing without passphrase works because account is temp unlocked 169 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 170 if err != nil { 171 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 172 } 173 174 // reset unlock to a shorter period, invalidates the previous unlock 175 if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { 176 t.Fatal(err) 177 } 178 179 // Signing without passphrase still works because account is temp unlocked 180 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 181 if err != nil { 182 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 183 } 184 185 // Signing fails again after automatic locking 186 time.Sleep(250 * time.Millisecond) 187 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 188 if err != ErrLocked { 189 t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) 190 } 191 } 192 193 // This test should fail under -race if signing races the expiration goroutine. 194 func TestSignRace(t *testing.T) { 195 t.Parallel() 196 _, ks := tmpKeyStore(t) 197 198 // Create a test account. 199 a1, err := ks.NewAccount("") 200 if err != nil { 201 t.Fatal("could not create the test account", err) 202 } 203 204 if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil { 205 t.Fatal("could not unlock the test account", err) 206 } 207 end := time.Now().Add(500 * time.Millisecond) 208 for time.Now().Before(end) { 209 if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked { 210 return 211 } else if err != nil { 212 t.Errorf("Sign error: %v", err) 213 return 214 } 215 time.Sleep(1 * time.Millisecond) 216 } 217 t.Errorf("Account did not lock within the timeout") 218 } 219 220 // waitForKsUpdating waits until the updating-status of the ks reaches the 221 // desired wantStatus. 222 // It waits for a maximum time of maxTime, and returns false if it does not 223 // finish in time 224 func waitForKsUpdating(t *testing.T, ks *KeyStore, wantStatus bool, maxTime time.Duration) bool { 225 t.Helper() 226 // Wait max 250 ms, then return false 227 for t0 := time.Now(); time.Since(t0) < maxTime; { 228 if ks.isUpdating() == wantStatus { 229 return true 230 } 231 time.Sleep(25 * time.Millisecond) 232 } 233 return false 234 } 235 236 // Tests that the wallet notifier loop starts and stops correctly based on the 237 // addition and removal of wallet event subscriptions. 238 func TestWalletNotifierLifecycle(t *testing.T) { 239 t.Parallel() 240 // Create a temporary keystore to test with 241 _, ks := tmpKeyStore(t) 242 243 // Ensure that the notification updater is not running yet 244 time.Sleep(250 * time.Millisecond) 245 246 if ks.isUpdating() { 247 t.Errorf("wallet notifier running without subscribers") 248 } 249 // Subscribe to the wallet feed and ensure the updater boots up 250 updates := make(chan accounts.WalletEvent) 251 252 subs := make([]event.Subscription, 2) 253 for i := 0; i < len(subs); i++ { 254 // Create a new subscription 255 subs[i] = ks.Subscribe(updates) 256 if !waitForKsUpdating(t, ks, true, 250*time.Millisecond) { 257 t.Errorf("sub %d: wallet notifier not running after subscription", i) 258 } 259 } 260 // Close all but one sub 261 for i := 0; i < len(subs)-1; i++ { 262 // Close an existing subscription 263 subs[i].Unsubscribe() 264 } 265 // Check that it is still running 266 time.Sleep(250 * time.Millisecond) 267 268 if !ks.isUpdating() { 269 t.Fatal("event notifier stopped prematurely") 270 } 271 // Unsubscribe the last one and ensure the updater terminates eventually. 272 subs[len(subs)-1].Unsubscribe() 273 if !waitForKsUpdating(t, ks, false, 4*time.Second) { 274 t.Errorf("wallet notifier didn't terminate after unsubscribe") 275 } 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 t.Parallel() 287 _, ks := tmpKeyStore(t) 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 // TestImportECDSA tests the import functionality of a keystore. 347 func TestImportECDSA(t *testing.T) { 348 t.Parallel() 349 _, ks := tmpKeyStore(t) 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 // TestImportExport tests the import and export functionality of a keystore. 366 func TestImportExport(t *testing.T) { 367 t.Parallel() 368 _, ks := tmpKeyStore(t) 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 _, ks2 := tmpKeyStore(t) 378 if _, err = ks2.Import(json, "old", "old"); err == nil { 379 t.Errorf("importing with invalid password succeeded") 380 } 381 acc2, err := ks2.Import(json, "new", "new") 382 if err != nil { 383 t.Errorf("importing failed: %v", err) 384 } 385 if acc.Address != acc2.Address { 386 t.Error("imported account does not match exported account") 387 } 388 if _, err = ks2.Import(json, "new", "new"); err == nil { 389 t.Errorf("importing a key twice succeeded") 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 t.Parallel() 397 _, ks := tmpKeyStore(t) 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 _, ks2 := tmpKeyStore(t) 407 var atom atomic.Uint32 408 var wg sync.WaitGroup 409 wg.Add(2) 410 for i := 0; i < 2; i++ { 411 go func() { 412 defer wg.Done() 413 if _, err := ks2.Import(json, "new", "new"); err != nil { 414 atom.Add(1) 415 } 416 }() 417 } 418 wg.Wait() 419 if atom.Load() != 1 { 420 t.Errorf("Import is racy") 421 } 422 } 423 424 // checkAccounts checks that all known live accounts are present in the wallet list. 425 func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) { 426 if len(live) != len(wallets) { 427 t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live)) 428 return 429 } 430 liveList := make([]accounts.Account, 0, len(live)) 431 for _, account := range live { 432 liveList = append(liveList, account) 433 } 434 slices.SortFunc(liveList, byURL) 435 for j, wallet := range wallets { 436 if accs := wallet.Accounts(); len(accs) != 1 { 437 t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs)) 438 } else if accs[0] != liveList[j] { 439 t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j]) 440 } 441 } 442 } 443 444 // checkEvents checks that all events in 'want' are present in 'have'. Events may be present multiple times. 445 func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) { 446 for _, wantEv := range want { 447 nmatch := 0 448 for ; len(have) > 0; nmatch++ { 449 if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a { 450 break 451 } 452 have = have[1:] 453 } 454 if nmatch == 0 { 455 t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address) 456 } 457 } 458 } 459 460 func tmpKeyStore(t *testing.T) (string, *KeyStore) { 461 d := t.TempDir() 462 return d, NewKeyStore(d, veryLightScryptN, veryLightScryptP) 463 }