account_cache.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // Copyright 2017 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. "bufio"
  19. "encoding/json"
  20. "fmt"
  21. "io/ioutil"
  22. "os"
  23. "path/filepath"
  24. "sort"
  25. "strings"
  26. "sync"
  27. "time"
  28. "github.com/ethereum/go-ethereum/accounts"
  29. "github.com/ethereum/go-ethereum/common"
  30. "github.com/ethereum/go-ethereum/log"
  31. )
  32. // Minimum amount of time between cache reloads. This limit applies if the platform does
  33. // not support change notifications. It also applies if the keystore directory does not
  34. // exist yet, the code will attempt to create a watcher at most this often.
  35. const minReloadInterval = 2 * time.Second
  36. type accountsByURL []accounts.Account
  37. func (s accountsByURL) Len() int { return len(s) }
  38. func (s accountsByURL) Less(i, j int) bool { return s[i].URL.Cmp(s[j].URL) < 0 }
  39. func (s accountsByURL) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  40. // AmbiguousAddrError is returned when attempting to unlock
  41. // an address for which more than one file exists.
  42. type AmbiguousAddrError struct {
  43. Addr common.Address
  44. Matches []accounts.Account
  45. }
  46. func (err *AmbiguousAddrError) Error() string {
  47. files := ""
  48. for i, a := range err.Matches {
  49. files += a.URL.Path
  50. if i < len(err.Matches)-1 {
  51. files += ", "
  52. }
  53. }
  54. return fmt.Sprintf("multiple keys match address (%s)", files)
  55. }
  56. // accountCache is a live index of all accounts in the keystore.
  57. type accountCache struct {
  58. keydir string
  59. watcher *watcher
  60. mu sync.Mutex
  61. all accountsByURL
  62. byAddr map[common.Address][]accounts.Account
  63. throttle *time.Timer
  64. notify chan struct{}
  65. }
  66. func newAccountCache(keydir string) (*accountCache, chan struct{}) {
  67. ac := &accountCache{
  68. keydir: keydir,
  69. byAddr: make(map[common.Address][]accounts.Account),
  70. notify: make(chan struct{}, 1),
  71. }
  72. ac.watcher = newWatcher(ac)
  73. return ac, ac.notify
  74. }
  75. func (ac *accountCache) accounts() []accounts.Account {
  76. ac.maybeReload()
  77. ac.mu.Lock()
  78. defer ac.mu.Unlock()
  79. cpy := make([]accounts.Account, len(ac.all))
  80. copy(cpy, ac.all)
  81. return cpy
  82. }
  83. func (ac *accountCache) hasAddress(addr common.Address) bool {
  84. ac.maybeReload()
  85. ac.mu.Lock()
  86. defer ac.mu.Unlock()
  87. return len(ac.byAddr[addr]) > 0
  88. }
  89. func (ac *accountCache) add(newAccount accounts.Account) {
  90. ac.mu.Lock()
  91. defer ac.mu.Unlock()
  92. i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].URL.Cmp(newAccount.URL) >= 0 })
  93. if i < len(ac.all) && ac.all[i] == newAccount {
  94. return
  95. }
  96. // newAccount is not in the cache.
  97. ac.all = append(ac.all, accounts.Account{})
  98. copy(ac.all[i+1:], ac.all[i:])
  99. ac.all[i] = newAccount
  100. ac.byAddr[newAccount.Address] = append(ac.byAddr[newAccount.Address], newAccount)
  101. }
  102. // note: removed needs to be unique here (i.e. both File and Address must be set).
  103. func (ac *accountCache) delete(removed accounts.Account) {
  104. ac.mu.Lock()
  105. defer ac.mu.Unlock()
  106. ac.all = removeAccount(ac.all, removed)
  107. if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
  108. delete(ac.byAddr, removed.Address)
  109. } else {
  110. ac.byAddr[removed.Address] = ba
  111. }
  112. }
  113. func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
  114. for i := range slice {
  115. if slice[i] == elem {
  116. return append(slice[:i], slice[i+1:]...)
  117. }
  118. }
  119. return slice
  120. }
  121. // find returns the cached account for address if there is a unique match.
  122. // The exact matching rules are explained by the documentation of accounts.Account.
  123. // Callers must hold ac.mu.
  124. func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
  125. // Limit search to address candidates if possible.
  126. matches := ac.all
  127. if (a.Address != common.Address{}) {
  128. matches = ac.byAddr[a.Address]
  129. }
  130. if a.URL.Path != "" {
  131. // If only the basename is specified, complete the path.
  132. if !strings.ContainsRune(a.URL.Path, filepath.Separator) {
  133. a.URL.Path = filepath.Join(ac.keydir, a.URL.Path)
  134. }
  135. for i := range matches {
  136. if matches[i].URL == a.URL {
  137. return matches[i], nil
  138. }
  139. }
  140. if (a.Address == common.Address{}) {
  141. return accounts.Account{}, ErrNoMatch
  142. }
  143. }
  144. switch len(matches) {
  145. case 1:
  146. return matches[0], nil
  147. case 0:
  148. return accounts.Account{}, ErrNoMatch
  149. default:
  150. err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
  151. copy(err.Matches, matches)
  152. return accounts.Account{}, err
  153. }
  154. }
  155. func (ac *accountCache) maybeReload() {
  156. ac.mu.Lock()
  157. defer ac.mu.Unlock()
  158. if ac.watcher.running {
  159. return // A watcher is running and will keep the cache up-to-date.
  160. }
  161. if ac.throttle == nil {
  162. ac.throttle = time.NewTimer(0)
  163. } else {
  164. select {
  165. case <-ac.throttle.C:
  166. default:
  167. return // The cache was reloaded recently.
  168. }
  169. }
  170. ac.watcher.start()
  171. ac.reload()
  172. ac.throttle.Reset(minReloadInterval)
  173. }
  174. func (ac *accountCache) close() {
  175. ac.mu.Lock()
  176. ac.watcher.close()
  177. if ac.throttle != nil {
  178. ac.throttle.Stop()
  179. }
  180. if ac.notify != nil {
  181. close(ac.notify)
  182. ac.notify = nil
  183. }
  184. ac.mu.Unlock()
  185. }
  186. // reload caches addresses of existing accounts.
  187. // Callers must hold ac.mu.
  188. func (ac *accountCache) reload() {
  189. accounts, err := ac.scan()
  190. if err != nil {
  191. log.Debug("Failed to reload keystore contents", "err", err)
  192. }
  193. ac.all = accounts
  194. sort.Sort(ac.all)
  195. for k := range ac.byAddr {
  196. delete(ac.byAddr, k)
  197. }
  198. for _, a := range accounts {
  199. ac.byAddr[a.Address] = append(ac.byAddr[a.Address], a)
  200. }
  201. select {
  202. case ac.notify <- struct{}{}:
  203. default:
  204. }
  205. log.Debug("Reloaded keystore contents", "accounts", len(ac.all))
  206. }
  207. func (ac *accountCache) scan() ([]accounts.Account, error) {
  208. files, err := ioutil.ReadDir(ac.keydir)
  209. if err != nil {
  210. return nil, err
  211. }
  212. var (
  213. buf = new(bufio.Reader)
  214. addrs []accounts.Account
  215. keyJSON struct {
  216. Address string `json:"address"`
  217. }
  218. )
  219. for _, fi := range files {
  220. path := filepath.Join(ac.keydir, fi.Name())
  221. if skipKeyFile(fi) {
  222. log.Trace("Ignoring file on account scan", "path", path)
  223. continue
  224. }
  225. logger := log.New("path", path)
  226. fd, err := os.Open(path)
  227. if err != nil {
  228. logger.Trace("Failed to open keystore file", "err", err)
  229. continue
  230. }
  231. buf.Reset(fd)
  232. // Parse the address.
  233. keyJSON.Address = ""
  234. err = json.NewDecoder(buf).Decode(&keyJSON)
  235. addr := common.HexToAddress(keyJSON.Address)
  236. switch {
  237. case err != nil:
  238. logger.Debug("Failed to decode keystore key", "err", err)
  239. case (addr == common.Address{}):
  240. logger.Debug("Failed to decode keystore key", "err", "missing or zero address")
  241. default:
  242. addrs = append(addrs, accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}})
  243. }
  244. fd.Close()
  245. }
  246. return addrs, err
  247. }
  248. func skipKeyFile(fi os.FileInfo) bool {
  249. // Skip editor backups and UNIX-style hidden files.
  250. if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
  251. return true
  252. }
  253. // Skip misc special files, directories (yes, symlinks too).
  254. if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
  255. return true
  256. }
  257. return false
  258. }