github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/accounts/addrcache_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 package accounts 18 19 import ( 20 "fmt" 21 "math/rand" 22 "os" 23 "path/filepath" 24 "reflect" 25 "sort" 26 "testing" 27 "time" 28 29 "github.com/cespare/cp" 30 "github.com/davecgh/go-spew/spew" 31 "github.com/ethereum/go-ethereum/common" 32 ) 33 34 var ( 35 cachetestDir, _ = filepath.Abs(filepath.Join("testdata", "keystore")) 36 cachetestAccounts = []Account{ 37 { 38 Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), 39 File: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), 40 }, 41 { 42 Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), 43 File: filepath.Join(cachetestDir, "aaa"), 44 }, 45 { 46 Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"), 47 File: filepath.Join(cachetestDir, "zzz"), 48 }, 49 } 50 ) 51 52 func TestWatchNewFile(t *testing.T) { 53 t.Parallel() 54 55 dir, am := tmpManager(t, false) 56 defer os.RemoveAll(dir) 57 58 // Ensure the watcher is started before adding any files. 59 am.Accounts() 60 time.Sleep(200 * time.Millisecond) 61 62 // Move in the files. 63 wantAccounts := make([]Account, len(cachetestAccounts)) 64 for i := range cachetestAccounts { 65 a := cachetestAccounts[i] 66 a.File = filepath.Join(dir, filepath.Base(a.File)) 67 wantAccounts[i] = a 68 if err := cp.CopyFile(a.File, cachetestAccounts[i].File); err != nil { 69 t.Fatal(err) 70 } 71 } 72 73 // am should see the accounts. 74 var list []Account 75 for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 { 76 list = am.Accounts() 77 if reflect.DeepEqual(list, wantAccounts) { 78 return 79 } 80 time.Sleep(d) 81 } 82 t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts)) 83 } 84 85 func TestWatchNoDir(t *testing.T) { 86 t.Parallel() 87 88 // Create am but not the directory that it watches. 89 rand.Seed(time.Now().UnixNano()) 90 dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int())) 91 am := NewManager(dir, LightScryptN, LightScryptP) 92 93 list := am.Accounts() 94 if len(list) > 0 { 95 t.Error("initial account list not empty:", list) 96 } 97 time.Sleep(100 * time.Millisecond) 98 99 // Create the directory and copy a key file into it. 100 os.MkdirAll(dir, 0700) 101 defer os.RemoveAll(dir) 102 file := filepath.Join(dir, "aaa") 103 if err := cp.CopyFile(file, cachetestAccounts[0].File); err != nil { 104 t.Fatal(err) 105 } 106 107 // am should see the account. 108 wantAccounts := []Account{cachetestAccounts[0]} 109 wantAccounts[0].File = file 110 for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 { 111 list = am.Accounts() 112 if reflect.DeepEqual(list, wantAccounts) { 113 return 114 } 115 time.Sleep(d) 116 } 117 t.Errorf("\ngot %v\nwant %v", list, wantAccounts) 118 } 119 120 func TestCacheInitialReload(t *testing.T) { 121 cache := newAddrCache(cachetestDir) 122 accounts := cache.accounts() 123 if !reflect.DeepEqual(accounts, cachetestAccounts) { 124 t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts)) 125 } 126 } 127 128 func TestCacheAddDeleteOrder(t *testing.T) { 129 cache := newAddrCache("testdata/no-such-dir") 130 cache.watcher.running = true // prevent unexpected reloads 131 132 accounts := []Account{ 133 { 134 Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), 135 File: "-309830980", 136 }, 137 { 138 Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), 139 File: "ggg", 140 }, 141 { 142 Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), 143 File: "zzzzzz-the-very-last-one.keyXXX", 144 }, 145 { 146 Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), 147 File: "SOMETHING.key", 148 }, 149 { 150 Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), 151 File: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8", 152 }, 153 { 154 Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), 155 File: "aaa", 156 }, 157 { 158 Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"), 159 File: "zzz", 160 }, 161 } 162 for _, a := range accounts { 163 cache.add(a) 164 } 165 // Add some of them twice to check that they don't get reinserted. 166 cache.add(accounts[0]) 167 cache.add(accounts[2]) 168 169 // Check that the account list is sorted by filename. 170 wantAccounts := make([]Account, len(accounts)) 171 copy(wantAccounts, accounts) 172 sort.Sort(accountsByFile(wantAccounts)) 173 list := cache.accounts() 174 if !reflect.DeepEqual(list, wantAccounts) { 175 t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accounts), spew.Sdump(wantAccounts)) 176 } 177 for _, a := range accounts { 178 if !cache.hasAddress(a.Address) { 179 t.Errorf("expected hasAccount(%x) to return true", a.Address) 180 } 181 } 182 if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) { 183 t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) 184 } 185 186 // Delete a few keys from the cache. 187 for i := 0; i < len(accounts); i += 2 { 188 cache.delete(wantAccounts[i]) 189 } 190 cache.delete(Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), File: "something"}) 191 192 // Check content again after deletion. 193 wantAccountsAfterDelete := []Account{ 194 wantAccounts[1], 195 wantAccounts[3], 196 wantAccounts[5], 197 } 198 list = cache.accounts() 199 if !reflect.DeepEqual(list, wantAccountsAfterDelete) { 200 t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete)) 201 } 202 for _, a := range wantAccountsAfterDelete { 203 if !cache.hasAddress(a.Address) { 204 t.Errorf("expected hasAccount(%x) to return true", a.Address) 205 } 206 } 207 if cache.hasAddress(wantAccounts[0].Address) { 208 t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address) 209 } 210 } 211 212 func TestCacheFind(t *testing.T) { 213 dir := filepath.Join("testdata", "dir") 214 cache := newAddrCache(dir) 215 cache.watcher.running = true // prevent unexpected reloads 216 217 accounts := []Account{ 218 { 219 Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), 220 File: filepath.Join(dir, "a.key"), 221 }, 222 { 223 Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), 224 File: filepath.Join(dir, "b.key"), 225 }, 226 { 227 Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), 228 File: filepath.Join(dir, "c.key"), 229 }, 230 { 231 Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), 232 File: filepath.Join(dir, "c2.key"), 233 }, 234 } 235 for _, a := range accounts { 236 cache.add(a) 237 } 238 239 nomatchAccount := Account{ 240 Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), 241 File: filepath.Join(dir, "something"), 242 } 243 tests := []struct { 244 Query Account 245 WantResult Account 246 WantError error 247 }{ 248 // by address 249 {Query: Account{Address: accounts[0].Address}, WantResult: accounts[0]}, 250 // by file 251 {Query: Account{File: accounts[0].File}, WantResult: accounts[0]}, 252 // by basename 253 {Query: Account{File: filepath.Base(accounts[0].File)}, WantResult: accounts[0]}, 254 // by file and address 255 {Query: accounts[0], WantResult: accounts[0]}, 256 // ambiguous address, tie resolved by file 257 {Query: accounts[2], WantResult: accounts[2]}, 258 // ambiguous address error 259 { 260 Query: Account{Address: accounts[2].Address}, 261 WantError: &AmbiguousAddrError{ 262 Addr: accounts[2].Address, 263 Matches: []Account{accounts[2], accounts[3]}, 264 }, 265 }, 266 // no match error 267 {Query: nomatchAccount, WantError: ErrNoMatch}, 268 {Query: Account{File: nomatchAccount.File}, WantError: ErrNoMatch}, 269 {Query: Account{File: filepath.Base(nomatchAccount.File)}, WantError: ErrNoMatch}, 270 {Query: Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch}, 271 } 272 for i, test := range tests { 273 a, err := cache.find(test.Query) 274 if !reflect.DeepEqual(err, test.WantError) { 275 t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError) 276 continue 277 } 278 if a != test.WantResult { 279 t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult) 280 continue 281 } 282 } 283 }