github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/accounts/keystore/keystore_test.go (about) 1 package keystore 2 3 import ( 4 "io/ioutil" 5 "math/rand" 6 "os" 7 "runtime" 8 "sort" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/quickchainproject/quickchain/accounts" 14 "github.com/quickchainproject/quickchain/common" 15 "github.com/quickchainproject/quickchain/event" 16 ) 17 18 var testSigData = make([]byte, 32) 19 20 func TestKeyStore(t *testing.T) { 21 dir, ks := tmpKeyStore(t, true) 22 defer os.RemoveAll(dir) 23 24 a, err := ks.NewAccount("foo") 25 if err != nil { 26 t.Fatal(err) 27 } 28 if !strings.HasPrefix(a.URL.Path, dir) { 29 t.Errorf("account file %s doesn't have dir prefix", a.URL) 30 } 31 stat, err := os.Stat(a.URL.Path) 32 if err != nil { 33 t.Fatalf("account file %s doesn't exist (%v)", a.URL, err) 34 } 35 if runtime.GOOS != "windows" && stat.Mode() != 0600 { 36 t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600) 37 } 38 if !ks.HasAddress(a.Address) { 39 t.Errorf("HasAccount(%x) should've returned true", a.Address) 40 } 41 if err := ks.Update(a, "foo", "bar"); err != nil { 42 t.Errorf("Update error: %v", err) 43 } 44 if err := ks.Delete(a, "bar"); err != nil { 45 t.Errorf("Delete error: %v", err) 46 } 47 if common.FileExist(a.URL.Path) { 48 t.Errorf("account file %s should be gone after Delete", a.URL) 49 } 50 if ks.HasAddress(a.Address) { 51 t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address) 52 } 53 } 54 55 func TestSign(t *testing.T) { 56 dir, ks := tmpKeyStore(t, true) 57 defer os.RemoveAll(dir) 58 59 pass := "" // not used but required by API 60 a1, err := ks.NewAccount(pass) 61 if err != nil { 62 t.Fatal(err) 63 } 64 if err := ks.Unlock(a1, ""); err != nil { 65 t.Fatal(err) 66 } 67 if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil { 68 t.Fatal(err) 69 } 70 } 71 72 func TestSignWithPassphrase(t *testing.T) { 73 dir, ks := tmpKeyStore(t, true) 74 defer os.RemoveAll(dir) 75 76 pass := "passwd" 77 acc, err := ks.NewAccount(pass) 78 if err != nil { 79 t.Fatal(err) 80 } 81 82 if _, unlocked := ks.unlocked[acc.Address]; unlocked { 83 t.Fatal("expected account to be locked") 84 } 85 86 _, err = ks.SignHashWithPassphrase(acc, pass, testSigData) 87 if err != nil { 88 t.Fatal(err) 89 } 90 91 if _, unlocked := ks.unlocked[acc.Address]; unlocked { 92 t.Fatal("expected account to be locked") 93 } 94 95 if _, err = ks.SignHashWithPassphrase(acc, "invalid passwd", testSigData); err == nil { 96 t.Fatal("expected SignHashWithPassphrase to fail with invalid password") 97 } 98 } 99 100 func TestTimedUnlock(t *testing.T) { 101 dir, ks := tmpKeyStore(t, true) 102 defer os.RemoveAll(dir) 103 104 pass := "foo" 105 a1, err := ks.NewAccount(pass) 106 if err != nil { 107 t.Fatal(err) 108 } 109 110 // Signing without passphrase fails because account is locked 111 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 112 if err != ErrLocked { 113 t.Fatal("Signing should've failed with ErrLocked before unlocking, got ", err) 114 } 115 116 // Signing with passphrase works 117 if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { 118 t.Fatal(err) 119 } 120 121 // Signing without passphrase works because account is temp unlocked 122 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 123 if err != nil { 124 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 125 } 126 127 // Signing fails again after automatic locking 128 time.Sleep(250 * time.Millisecond) 129 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 130 if err != ErrLocked { 131 t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) 132 } 133 } 134 135 func TestOverrideUnlock(t *testing.T) { 136 dir, ks := tmpKeyStore(t, false) 137 defer os.RemoveAll(dir) 138 139 pass := "foo" 140 a1, err := ks.NewAccount(pass) 141 if err != nil { 142 t.Fatal(err) 143 } 144 145 // Unlock indefinitely. 146 if err = ks.TimedUnlock(a1, pass, 5*time.Minute); err != nil { 147 t.Fatal(err) 148 } 149 150 // Signing without passphrase works because account is temp unlocked 151 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 152 if err != nil { 153 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 154 } 155 156 // reset unlock to a shorter period, invalidates the previous unlock 157 if err = ks.TimedUnlock(a1, pass, 100*time.Millisecond); err != nil { 158 t.Fatal(err) 159 } 160 161 // Signing without passphrase still works because account is temp unlocked 162 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 163 if err != nil { 164 t.Fatal("Signing shouldn't return an error after unlocking, got ", err) 165 } 166 167 // Signing fails again after automatic locking 168 time.Sleep(250 * time.Millisecond) 169 _, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData) 170 if err != ErrLocked { 171 t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err) 172 } 173 } 174 175 // This test should fail under -race if signing races the expiration goroutine. 176 func TestSignRace(t *testing.T) { 177 dir, ks := tmpKeyStore(t, false) 178 defer os.RemoveAll(dir) 179 180 // Create a test account. 181 a1, err := ks.NewAccount("") 182 if err != nil { 183 t.Fatal("could not create the test account", err) 184 } 185 186 if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil { 187 t.Fatal("could not unlock the test account", err) 188 } 189 end := time.Now().Add(500 * time.Millisecond) 190 for time.Now().Before(end) { 191 if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked { 192 return 193 } else if err != nil { 194 t.Errorf("Sign error: %v", err) 195 return 196 } 197 time.Sleep(1 * time.Millisecond) 198 } 199 t.Errorf("Account did not lock within the timeout") 200 } 201 202 // Tests that the wallet notifier loop starts and stops correctly based on the 203 // addition and removal of wallet event subscriptions. 204 func TestWalletNotifierLifecycle(t *testing.T) { 205 // Create a temporary kesytore to test with 206 dir, ks := tmpKeyStore(t, false) 207 defer os.RemoveAll(dir) 208 209 // Ensure that the notification updater is not running yet 210 time.Sleep(250 * time.Millisecond) 211 ks.mu.RLock() 212 updating := ks.updating 213 ks.mu.RUnlock() 214 215 if updating { 216 t.Errorf("wallet notifier running without subscribers") 217 } 218 // Subscribe to the wallet feed and ensure the updater boots up 219 updates := make(chan accounts.WalletEvent) 220 221 subs := make([]event.Subscription, 2) 222 for i := 0; i < len(subs); i++ { 223 // Create a new subscription 224 subs[i] = ks.Subscribe(updates) 225 226 // Ensure the notifier comes online 227 time.Sleep(250 * time.Millisecond) 228 ks.mu.RLock() 229 updating = ks.updating 230 ks.mu.RUnlock() 231 232 if !updating { 233 t.Errorf("sub %d: wallet notifier not running after subscription", i) 234 } 235 } 236 // Unsubscribe and ensure the updater terminates eventually 237 for i := 0; i < len(subs); i++ { 238 // Close an existing subscription 239 subs[i].Unsubscribe() 240 241 // Ensure the notifier shuts down at and only at the last close 242 for k := 0; k < int(walletRefreshCycle/(250*time.Millisecond))+2; k++ { 243 ks.mu.RLock() 244 updating = ks.updating 245 ks.mu.RUnlock() 246 247 if i < len(subs)-1 && !updating { 248 t.Fatalf("sub %d: event notifier stopped prematurely", i) 249 } 250 if i == len(subs)-1 && !updating { 251 return 252 } 253 time.Sleep(250 * time.Millisecond) 254 } 255 } 256 t.Errorf("wallet notifier didn't terminate after unsubscribe") 257 } 258 259 type walletEvent struct { 260 accounts.WalletEvent 261 a accounts.Account 262 } 263 264 // Tests that wallet notifications and correctly fired when accounts are added 265 // or deleted from the keystore. 266 func TestWalletNotifications(t *testing.T) { 267 dir, ks := tmpKeyStore(t, false) 268 defer os.RemoveAll(dir) 269 270 // Subscribe to the wallet feed and collect events. 271 var ( 272 events []walletEvent 273 updates = make(chan accounts.WalletEvent) 274 sub = ks.Subscribe(updates) 275 ) 276 defer sub.Unsubscribe() 277 go func() { 278 for { 279 select { 280 case ev := <-updates: 281 events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]}) 282 case <-sub.Err(): 283 close(updates) 284 return 285 } 286 } 287 }() 288 289 // Randomly add and remove accounts. 290 var ( 291 live = make(map[common.Address]accounts.Account) 292 wantEvents []walletEvent 293 ) 294 for i := 0; i < 1024; i++ { 295 if create := len(live) == 0 || rand.Int()%4 > 0; create { 296 // Add a new account and ensure wallet notifications arrives 297 account, err := ks.NewAccount("") 298 if err != nil { 299 t.Fatalf("failed to create test account: %v", err) 300 } 301 live[account.Address] = account 302 wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account}) 303 } else { 304 // Delete a random account. 305 var account accounts.Account 306 for _, a := range live { 307 account = a 308 break 309 } 310 if err := ks.Delete(account, ""); err != nil { 311 t.Fatalf("failed to delete test account: %v", err) 312 } 313 delete(live, account.Address) 314 wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account}) 315 } 316 } 317 318 // Shut down the event collector and check events. 319 sub.Unsubscribe() 320 <-updates 321 checkAccounts(t, live, ks.Wallets()) 322 checkEvents(t, wantEvents, events) 323 } 324 325 // checkAccounts checks that all known live accounts are present in the wallet list. 326 func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, wallets []accounts.Wallet) { 327 if len(live) != len(wallets) { 328 t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live)) 329 return 330 } 331 liveList := make([]accounts.Account, 0, len(live)) 332 for _, account := range live { 333 liveList = append(liveList, account) 334 } 335 sort.Sort(accountsByURL(liveList)) 336 for j, wallet := range wallets { 337 if accs := wallet.Accounts(); len(accs) != 1 { 338 t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs)) 339 } else if accs[0] != liveList[j] { 340 t.Errorf("wallet %d: account mismatch: have %v, want %v", j, accs[0], liveList[j]) 341 } 342 } 343 } 344 345 // checkEvents checks that all events in 'want' are present in 'have'. Events may be present multiple times. 346 func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) { 347 for _, wantEv := range want { 348 nmatch := 0 349 for ; len(have) > 0; nmatch++ { 350 if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a { 351 break 352 } 353 have = have[1:] 354 } 355 if nmatch == 0 { 356 t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address) 357 } 358 } 359 } 360 361 func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) { 362 d, err := ioutil.TempDir("", "eth-keystore-test") 363 if err != nil { 364 t.Fatal(err) 365 } 366 new := NewPlaintextKeyStore 367 if encrypted { 368 new = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) } 369 } 370 return d, new(d) 371 }