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