addrcache_test.go 8.5 KB

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