github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/accounts/cache_test.go (about) 1 // Copyright 2016 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 // +build !deterministic 18 19 package accounts 20 21 import ( 22 "fmt" 23 "io/ioutil" 24 "math/rand" 25 "os" 26 "path/filepath" 27 "reflect" 28 "sort" 29 "testing" 30 "time" 31 32 "github.com/davecgh/go-spew/spew" 33 "github.com/ethereumproject/go-ethereum/common" 34 ) 35 36 func TestWatchNewFile(t *testing.T) { 37 t.Parallel() 38 39 dir, am := tmpManager(t) 40 defer os.RemoveAll(dir) 41 42 // Ensure the watcher is started before adding any files. 43 am.Accounts() 44 time.Sleep(5 * time.Second) 45 if w := am.ac.getWatcher(); !w.running { 46 t.Fatalf("watcher not running after %v: %v", 5*time.Second, spew.Sdump(w)) 47 } 48 49 // Move in the files. 50 wantAccounts := make([]Account, len(cachetestAccounts)) 51 for i := range cachetestAccounts { 52 a := cachetestAccounts[i] 53 a.File = filepath.Join(dir, filepath.Base(a.File)) // rename test file to base from temp dir 54 wantAccounts[i] = a 55 data, err := ioutil.ReadFile(cachetestAccounts[i].File) // but we still have to read from original file path 56 if err != nil { 57 t.Fatal(err) 58 } 59 // write to temp dir 60 if err := ioutil.WriteFile(a.File, data, 0666); err != nil { 61 t.Fatal(err) 62 } 63 } 64 sort.Sort(accountsByFile(wantAccounts)) 65 66 // am should see the accounts. 67 var list []Account 68 for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 { 69 list = am.Accounts() 70 if reflect.DeepEqual(list, wantAccounts) { 71 return 72 } 73 time.Sleep(d) 74 } 75 t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts)) 76 } 77 78 func TestWatchNoDir(t *testing.T) { 79 t.Parallel() 80 81 // Create am but not the directory that it watches. 82 rand.Seed(time.Now().UnixNano()) 83 rp := fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()) 84 dir, e := ioutil.TempDir("", rp) 85 if e != nil { 86 t.Fatal(e) 87 } 88 defer os.RemoveAll(dir) 89 90 am, err := NewManager(dir, LightScryptN, LightScryptP, false) 91 if err != nil { 92 t.Fatal(err) 93 } 94 95 list := am.Accounts() 96 if len(list) > 0 { 97 t.Error("initial account list not empty:", list) 98 } 99 time.Sleep(5 * time.Second) 100 if w := am.ac.getWatcher(); !w.running { 101 t.Fatalf("watcher not running after %v: %v", 5*time.Second, spew.Sdump(w)) 102 } 103 104 // Create the directory and copy a key file into it. 105 os.MkdirAll(dir, 0700) 106 defer os.RemoveAll(dir) 107 108 file := filepath.Join(dir, "aaa") 109 // This filepath-ing is redundant but ensure parallel tests don't fuck each other up... I think. 110 data, err := ioutil.ReadFile(filepath.Join(cachetestDir, filepath.Base(cachetestAccounts[0].File))) 111 if err != nil { 112 t.Fatal(err) 113 } 114 ff, err := os.Create(file) 115 if err != nil { 116 t.Fatal(err) 117 } 118 _, err = ff.Write(data) 119 if err != nil { 120 t.Fatal(err) 121 } 122 ff.Close() 123 124 // am should see the account. 125 a := cachetestAccounts[0] 126 a.File = file 127 wantAccounts := []Account{a} 128 var gotAccounts []Account 129 for d := 500 * time.Millisecond; d <= 2*minReloadInterval; d *= 2 { 130 gotAccounts = am.Accounts() 131 if reflect.DeepEqual(gotAccounts, wantAccounts) { 132 return 133 } 134 time.Sleep(d) 135 } 136 t.Errorf("account watcher never saw changes: got: %v, want: %v", spew.Sdump(gotAccounts), spew.Sdump(wantAccounts)) 137 } 138 139 func TestCacheInitialReload(t *testing.T) { 140 141 cache := newAddrCache(cachetestDir) 142 143 accounts := cache.accounts() 144 145 if !reflect.DeepEqual(accounts, cachetestAccounts) { 146 t.Errorf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts)) 147 } 148 } 149 150 func TestCacheAddDeleteOrder(t *testing.T) { 151 cache := newAddrCache("testdata/no-such-dir") 152 defer os.RemoveAll("testdata/no-such-dir") 153 cache.watcher.running = true // prevent unexpected reloads 154 155 accounts := []Account{ 156 { 157 Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), 158 File: "-309830980", 159 }, 160 { 161 Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), 162 File: "ggg", 163 }, 164 { 165 Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), 166 File: "zzzzzz-the-very-last-one.keyXXX", 167 }, 168 { 169 Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), 170 File: "SOMETHING.key", 171 }, 172 { 173 Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), 174 File: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8", 175 }, 176 { 177 Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), 178 File: "aaa", 179 }, 180 { 181 Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"), 182 File: "zzz", 183 }, 184 } 185 for _, a := range accounts { 186 cache.add(a) 187 } 188 // Add some of them twice to check that they don't get reinserted. 189 cache.add(accounts[0]) 190 cache.add(accounts[2]) 191 192 // Check that the account list is sorted by filename. 193 wantAccounts := make([]Account, len(accounts)) 194 copy(wantAccounts, accounts) 195 sort.Sort(accountsByFile(wantAccounts)) 196 list := cache.accounts() 197 if !reflect.DeepEqual(list, wantAccounts) { 198 t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accounts), spew.Sdump(wantAccounts)) 199 } 200 for _, a := range accounts { 201 if !cache.hasAddress(a.Address) { 202 t.Errorf("expected hasAccount(%x) to return true", a.Address) 203 } 204 } 205 if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) { 206 t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) 207 } 208 209 // Delete a few keys from the cache. 210 for i := 0; i < len(accounts); i += 2 { 211 cache.delete(wantAccounts[i]) 212 } 213 cache.delete(Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), File: "something"}) 214 215 // Check content again after deletion. 216 wantAccountsAfterDelete := []Account{ 217 wantAccounts[1], 218 wantAccounts[3], 219 wantAccounts[5], 220 } 221 list = cache.accounts() 222 if !reflect.DeepEqual(list, wantAccountsAfterDelete) { 223 t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete)) 224 } 225 for _, a := range wantAccountsAfterDelete { 226 if !cache.hasAddress(a.Address) { 227 t.Errorf("expected hasAccount(%x) to return true", a.Address) 228 } 229 } 230 if cache.hasAddress(wantAccounts[0].Address) { 231 t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address) 232 } 233 } 234 235 func TestCacheFind(t *testing.T) { 236 dir := filepath.Join("testdata", "dir") 237 defer os.RemoveAll(dir) 238 cache := newAddrCache(dir) 239 cache.watcher.running = true // prevent unexpected reloads 240 241 accounts := []Account{ 242 { 243 Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), 244 File: filepath.Join(dir, "a.key"), 245 }, 246 { 247 Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), 248 File: filepath.Join(dir, "b.key"), 249 }, 250 { 251 Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), 252 File: filepath.Join(dir, "c.key"), 253 }, 254 { 255 Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), 256 File: filepath.Join(dir, "c2.key"), 257 }, 258 } 259 for _, a := range accounts { 260 cache.add(a) 261 } 262 263 if lca := cache.accounts(); len(lca) != len(accounts) { 264 t.Fatalf("wrong number of accounts, got: %v, want: %v", len(lca), len(accounts)) 265 } 266 267 if !reflect.DeepEqual(cache.accounts(), accounts) { 268 t.Fatalf("not matching initial accounts: got %v, want: %v", spew.Sdump(cache.accounts()), spew.Sdump(accounts)) 269 } 270 271 nomatchAccount := Account{ 272 Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), 273 File: filepath.Join(dir, "something"), 274 } 275 tests := []struct { 276 Query Account 277 WantResult Account 278 WantError error 279 }{ 280 // by address 281 {Query: Account{Address: accounts[0].Address}, WantResult: accounts[0]}, 282 // by file 283 {Query: Account{File: accounts[0].File}, WantResult: accounts[0]}, 284 // by basename 285 {Query: Account{File: filepath.Base(accounts[0].File)}, WantResult: accounts[0]}, 286 // by file and address 287 {Query: accounts[0], WantResult: accounts[0]}, 288 // ambiguous address, tie resolved by file 289 {Query: accounts[2], WantResult: accounts[2]}, 290 // ambiguous address error 291 { 292 Query: Account{Address: accounts[2].Address}, 293 WantError: &AmbiguousAddrError{ 294 Addr: accounts[2].Address, 295 Matches: []Account{accounts[2], accounts[3]}, 296 }, 297 }, 298 // no match error 299 {Query: nomatchAccount, WantError: ErrNoMatch}, 300 {Query: Account{File: nomatchAccount.File}, WantError: ErrNoMatch}, 301 {Query: Account{File: filepath.Base(nomatchAccount.File)}, WantError: ErrNoMatch}, 302 {Query: Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch}, 303 } 304 for i, test := range tests { 305 a, err := cache.find(test.Query) 306 if !reflect.DeepEqual(err, test.WantError) { 307 t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError) 308 continue 309 } 310 if a != test.WantResult { 311 t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult) 312 continue 313 } 314 } 315 } 316 317 func TestAccountCache_WatchRemove(t *testing.T) { 318 t.Parallel() 319 320 // setup temp dir 321 rp := fmt.Sprintf("eth-cacheremove-watch-test-%d-%d", os.Getpid(), rand.Int()) 322 tmpDir, e := ioutil.TempDir("", rp) 323 if e != nil { 324 t.Fatalf("create temp dir: %v", e) 325 } 326 defer os.RemoveAll(tmpDir) 327 328 // copy 3 test account files into temp dir 329 wantAccounts := make([]Account, len(cachetestAccounts)) 330 for i := range cachetestAccounts { 331 a := cachetestAccounts[i] 332 a.File = filepath.Join(tmpDir, filepath.Base(a.File)) 333 wantAccounts[i] = a 334 data, err := ioutil.ReadFile(cachetestAccounts[i].File) 335 if err != nil { 336 t.Fatal(err) 337 } 338 ff, err := os.Create(a.File) 339 if err != nil { 340 t.Fatal(err) 341 } 342 _, err = ff.Write(data) 343 if err != nil { 344 t.Fatal(err) 345 } 346 ff.Close() 347 } 348 349 // make manager in temp dir 350 ma, e := NewManager(tmpDir, veryLightScryptN, veryLightScryptP, false) 351 if e != nil { 352 t.Errorf("create manager in temp dir: %v", e) 353 } 354 355 // test manager has all accounts 356 initAccs := ma.Accounts() 357 if !reflect.DeepEqual(initAccs, wantAccounts) { 358 t.Errorf("got %v, want: %v", spew.Sdump(initAccs), spew.Sdump(wantAccounts)) 359 } 360 time.Sleep(minReloadInterval) 361 362 // test watcher is watching 363 if w := ma.ac.getWatcher(); !w.running { 364 t.Errorf("watcher not running") 365 } 366 367 // remove file 368 rmPath := filepath.Join(tmpDir, filepath.Base(wantAccounts[0].File)) 369 if e := os.Remove(rmPath); e != nil { 370 t.Fatalf("removing key file: %v", e) 371 } 372 // ensure it's gone 373 if _, e := os.Stat(rmPath); e == nil { 374 t.Fatalf("removed file not actually rm'd") 375 } 376 377 // test manager does not have account 378 wantAccounts = wantAccounts[1:] 379 if len(wantAccounts) != 2 { 380 t.Errorf("dummy") 381 } 382 383 gotAccounts := []Account{} 384 for d := 500 * time.Millisecond; d < 5*time.Second; d *= 2 { 385 gotAccounts = ma.Accounts() 386 // If it's ever all the same, we're good. Exit with aplomb. 387 if reflect.DeepEqual(gotAccounts, wantAccounts) { 388 return 389 } 390 time.Sleep(d) 391 } 392 t.Errorf("got: %v, want: %v", spew.Sdump(gotAccounts), spew.Sdump(wantAccounts)) 393 } 394 395 func TestCacheFilePath(t *testing.T) { 396 dir := filepath.Join("testdata", "keystore") 397 dir, _ = filepath.Abs(dir) 398 cache := newAddrCache(dir) 399 400 accs := cache.accounts() 401 402 for _, a := range accs { 403 if !filepath.IsAbs(a.File) { 404 t.Errorf("wanted absolute filepath, got: %v", a.File) 405 } 406 } 407 408 cache.close() 409 }