address_cache_test.go 8.7 KB

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