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