github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/accounts/keystore/account_cache_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:26</date> 10 //</624342584839376896> 11 12 13 package keystore 14 15 import ( 16 "fmt" 17 "io/ioutil" 18 "math/rand" 19 "os" 20 "path/filepath" 21 "reflect" 22 "sort" 23 "testing" 24 "time" 25 26 "github.com/cespare/cp" 27 "github.com/davecgh/go-spew/spew" 28 "github.com/ethereum/go-ethereum/accounts" 29 "github.com/ethereum/go-ethereum/common" 30 ) 31 32 var ( 33 cachetestDir, _ = filepath.Abs(filepath.Join("testdata", "keystore")) 34 cachetestAccounts = []accounts.Account{ 35 { 36 Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), 37 URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8")}, 38 }, 39 { 40 Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), 41 URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "aaa")}, 42 }, 43 { 44 Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"), 45 URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(cachetestDir, "zzz")}, 46 }, 47 } 48 ) 49 50 func TestWatchNewFile(t *testing.T) { 51 t.Parallel() 52 53 dir, ks := tmpKeyStore(t, false) 54 defer os.RemoveAll(dir) 55 56 //在添加任何文件之前,请确保监视程序已启动。 57 ks.Accounts() 58 time.Sleep(1000 * time.Millisecond) 59 60 //移入文件。 61 wantAccounts := make([]accounts.Account, len(cachetestAccounts)) 62 for i := range cachetestAccounts { 63 wantAccounts[i] = accounts.Account{ 64 Address: cachetestAccounts[i].Address, 65 URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, filepath.Base(cachetestAccounts[i].URL.Path))}, 66 } 67 if err := cp.CopyFile(wantAccounts[i].URL.Path, cachetestAccounts[i].URL.Path); err != nil { 68 t.Fatal(err) 69 } 70 } 71 72 //Ks应该看看账目。 73 var list []accounts.Account 74 for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 { 75 list = ks.Accounts() 76 if reflect.DeepEqual(list, wantAccounts) { 77 //KS也应该收到变更通知 78 select { 79 case <-ks.changes: 80 default: 81 t.Fatalf("wasn't notified of new accounts") 82 } 83 return 84 } 85 time.Sleep(d) 86 } 87 t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts)) 88 } 89 90 func TestWatchNoDir(t *testing.T) { 91 t.Parallel() 92 93 //创建ks,但不创建它监视的目录。 94 rand.Seed(time.Now().UnixNano()) 95 dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int())) 96 ks := NewKeyStore(dir, LightScryptN, LightScryptP) 97 98 list := ks.Accounts() 99 if len(list) > 0 { 100 t.Error("initial account list not empty:", list) 101 } 102 time.Sleep(100 * time.Millisecond) 103 104 //创建目录并将密钥文件复制到其中。 105 os.MkdirAll(dir, 0700) 106 defer os.RemoveAll(dir) 107 file := filepath.Join(dir, "aaa") 108 if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil { 109 t.Fatal(err) 110 } 111 112 //KS应该看到账户。 113 wantAccounts := []accounts.Account{cachetestAccounts[0]} 114 wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} 115 for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 { 116 list = ks.Accounts() 117 if reflect.DeepEqual(list, wantAccounts) { 118 //KS也应该收到变更通知 119 select { 120 case <-ks.changes: 121 default: 122 t.Fatalf("wasn't notified of new accounts") 123 } 124 return 125 } 126 time.Sleep(d) 127 } 128 t.Errorf("\ngot %v\nwant %v", list, wantAccounts) 129 } 130 131 func TestCacheInitialReload(t *testing.T) { 132 cache, _ := newAccountCache(cachetestDir) 133 accounts := cache.accounts() 134 if !reflect.DeepEqual(accounts, cachetestAccounts) { 135 t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts)) 136 } 137 } 138 139 func TestCacheAddDeleteOrder(t *testing.T) { 140 cache, _ := newAccountCache("testdata/no-such-dir") 141 cache.watcher.running = true //防止意外重新加载 142 143 accs := []accounts.Account{ 144 { 145 Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), 146 URL: accounts.URL{Scheme: KeyStoreScheme, Path: "-309830980"}, 147 }, 148 { 149 Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), 150 URL: accounts.URL{Scheme: KeyStoreScheme, Path: "ggg"}, 151 }, 152 { 153 Address: common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), 154 URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzzzzz-the-very-last-one.keyXXX"}, 155 }, 156 { 157 Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), 158 URL: accounts.URL{Scheme: KeyStoreScheme, Path: "SOMETHING.key"}, 159 }, 160 { 161 Address: common.HexToAddress("7ef5a6135f1fd6a02593eedc869c6d41d934aef8"), 162 URL: accounts.URL{Scheme: KeyStoreScheme, Path: "UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8"}, 163 }, 164 { 165 Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), 166 URL: accounts.URL{Scheme: KeyStoreScheme, Path: "aaa"}, 167 }, 168 { 169 Address: common.HexToAddress("289d485d9771714cce91d3393d764e1311907acc"), 170 URL: accounts.URL{Scheme: KeyStoreScheme, Path: "zzz"}, 171 }, 172 } 173 for _, a := range accs { 174 cache.add(a) 175 } 176 //添加其中一些两次以检查它们是否没有重新插入。 177 cache.add(accs[0]) 178 cache.add(accs[2]) 179 180 //检查帐户列表是否按文件名排序。 181 wantAccounts := make([]accounts.Account, len(accs)) 182 copy(wantAccounts, accs) 183 sort.Sort(accountsByURL(wantAccounts)) 184 list := cache.accounts() 185 if !reflect.DeepEqual(list, wantAccounts) { 186 t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts)) 187 } 188 for _, a := range accs { 189 if !cache.hasAddress(a.Address) { 190 t.Errorf("expected hasAccount(%x) to return true", a.Address) 191 } 192 } 193 if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) { 194 t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) 195 } 196 197 //从缓存中删除一些键。 198 for i := 0; i < len(accs); i += 2 { 199 cache.delete(wantAccounts[i]) 200 } 201 cache.delete(accounts.Account{Address: common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"), URL: accounts.URL{Scheme: KeyStoreScheme, Path: "something"}}) 202 203 //删除后再次检查内容。 204 wantAccountsAfterDelete := []accounts.Account{ 205 wantAccounts[1], 206 wantAccounts[3], 207 wantAccounts[5], 208 } 209 list = cache.accounts() 210 if !reflect.DeepEqual(list, wantAccountsAfterDelete) { 211 t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete)) 212 } 213 for _, a := range wantAccountsAfterDelete { 214 if !cache.hasAddress(a.Address) { 215 t.Errorf("expected hasAccount(%x) to return true", a.Address) 216 } 217 } 218 if cache.hasAddress(wantAccounts[0].Address) { 219 t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address) 220 } 221 } 222 223 func TestCacheFind(t *testing.T) { 224 dir := filepath.Join("testdata", "dir") 225 cache, _ := newAccountCache(dir) 226 cache.watcher.running = true //防止意外重新加载 227 228 accs := []accounts.Account{ 229 { 230 Address: common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), 231 URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "a.key")}, 232 }, 233 { 234 Address: common.HexToAddress("2cac1adea150210703ba75ed097ddfe24e14f213"), 235 URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "b.key")}, 236 }, 237 { 238 Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), 239 URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c.key")}, 240 }, 241 { 242 Address: common.HexToAddress("d49ff4eeb0b2686ed89c0fc0f2b6ea533ddbbd5e"), 243 URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "c2.key")}, 244 }, 245 } 246 for _, a := range accs { 247 cache.add(a) 248 } 249 250 nomatchAccount := accounts.Account{ 251 Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"), 252 URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")}, 253 } 254 tests := []struct { 255 Query accounts.Account 256 WantResult accounts.Account 257 WantError error 258 }{ 259 //按地址 260 {Query: accounts.Account{Address: accs[0].Address}, WantResult: accs[0]}, 261 //按文件 262 {Query: accounts.Account{URL: accs[0].URL}, WantResult: accs[0]}, 263 //用BaseNeMe 264 {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(accs[0].URL.Path)}}, WantResult: accs[0]}, 265 //按文件和地址 266 {Query: accs[0], WantResult: accs[0]}, 267 //地址不明确,由文件解析的领带 268 {Query: accs[2], WantResult: accs[2]}, 269 //地址不明确错误 270 { 271 Query: accounts.Account{Address: accs[2].Address}, 272 WantError: &AmbiguousAddrError{ 273 Addr: accs[2].Address, 274 Matches: []accounts.Account{accs[2], accs[3]}, 275 }, 276 }, 277 //无匹配误差 278 {Query: nomatchAccount, WantError: ErrNoMatch}, 279 {Query: accounts.Account{URL: nomatchAccount.URL}, WantError: ErrNoMatch}, 280 {Query: accounts.Account{URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Base(nomatchAccount.URL.Path)}}, WantError: ErrNoMatch}, 281 {Query: accounts.Account{Address: nomatchAccount.Address}, WantError: ErrNoMatch}, 282 } 283 for i, test := range tests { 284 a, err := cache.find(test.Query) 285 if !reflect.DeepEqual(err, test.WantError) { 286 t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError) 287 continue 288 } 289 if a != test.WantResult { 290 t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult) 291 continue 292 } 293 } 294 } 295 296 func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error { 297 var list []accounts.Account 298 for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 { 299 list = ks.Accounts() 300 if reflect.DeepEqual(list, wantAccounts) { 301 //KS也应该收到变更通知 302 select { 303 case <-ks.changes: 304 default: 305 return fmt.Errorf("wasn't notified of new accounts") 306 } 307 return nil 308 } 309 time.Sleep(d) 310 } 311 return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts) 312 } 313 314 //testUpdatedKeyFileContents测试更新密钥库文件的内容 315 //被观察者注意到,帐户缓存会相应地更新 316 func TestUpdatedKeyfileContents(t *testing.T) { 317 t.Parallel() 318 319 //创建要测试的临时Kesytore 320 rand.Seed(time.Now().UnixNano()) 321 dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int())) 322 ks := NewKeyStore(dir, LightScryptN, LightScryptP) 323 324 list := ks.Accounts() 325 if len(list) > 0 { 326 t.Error("initial account list not empty:", list) 327 } 328 time.Sleep(100 * time.Millisecond) 329 330 //创建目录并将密钥文件复制到其中。 331 os.MkdirAll(dir, 0700) 332 defer os.RemoveAll(dir) 333 file := filepath.Join(dir, "aaa") 334 335 //把我们的一个测试文件放在那里 336 if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil { 337 t.Fatal(err) 338 } 339 340 //KS应该看到账户。 341 wantAccounts := []accounts.Account{cachetestAccounts[0]} 342 wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} 343 if err := waitForAccounts(wantAccounts, ks); err != nil { 344 t.Error(err) 345 return 346 } 347 348 //需要,以便“file”的modtime与forcecopyfile之后的当前值不同 349 time.Sleep(1000 * time.Millisecond) 350 351 //现在替换文件内容 352 if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil { 353 t.Fatal(err) 354 return 355 } 356 wantAccounts = []accounts.Account{cachetestAccounts[1]} 357 wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} 358 if err := waitForAccounts(wantAccounts, ks); err != nil { 359 t.Errorf("First replacement failed") 360 t.Error(err) 361 return 362 } 363 364 //需要,以便“file”的modtime与forcecopyfile之后的当前值不同 365 time.Sleep(1000 * time.Millisecond) 366 367 //现在重新替换文件内容 368 if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil { 369 t.Fatal(err) 370 return 371 } 372 wantAccounts = []accounts.Account{cachetestAccounts[2]} 373 wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file} 374 if err := waitForAccounts(wantAccounts, ks); err != nil { 375 t.Errorf("Second replacement failed") 376 t.Error(err) 377 return 378 } 379 380 //需要,以便“file”的modtime与ioutil.writefile之后的当前值不同 381 time.Sleep(1000 * time.Millisecond) 382 383 //现在用垃圾替换文件内容 384 if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil { 385 t.Fatal(err) 386 return 387 } 388 if err := waitForAccounts([]accounts.Account{}, ks); err != nil { 389 t.Errorf("Emptying account file failed") 390 t.Error(err) 391 return 392 } 393 } 394 395 //forcecopyfile与cp.copyfile类似,但如果目标存在,则不会抱怨。 396 func forceCopyFile(dst, src string) error { 397 data, err := ioutil.ReadFile(src) 398 if err != nil { 399 return err 400 } 401 return ioutil.WriteFile(dst, data, 0644) 402 } 403