addrcache.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  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. "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/common"
  29. "github.com/ethereum/go-ethereum/logger"
  30. "github.com/ethereum/go-ethereum/logger/glog"
  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 accountsByFile []Account
  37. func (s accountsByFile) Len() int { return len(s) }
  38. func (s accountsByFile) Less(i, j int) bool { return s[i].File < s[j].File }
  39. func (s accountsByFile) 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 []Account
  45. }
  46. func (err *AmbiguousAddrError) Error() string {
  47. files := ""
  48. for i, a := range err.Matches {
  49. files += a.File
  50. if i < len(err.Matches)-1 {
  51. files += ", "
  52. }
  53. }
  54. return fmt.Sprintf("multiple keys match address (%s)", files)
  55. }
  56. // addrCache is a live index of all accounts in the keystore.
  57. type addrCache struct {
  58. keydir string
  59. watcher *watcher
  60. mu sync.Mutex
  61. all accountsByFile
  62. byAddr map[common.Address][]Account
  63. throttle *time.Timer
  64. }
  65. func newAddrCache(keydir string) *addrCache {
  66. ac := &addrCache{
  67. keydir: keydir,
  68. byAddr: make(map[common.Address][]Account),
  69. }
  70. ac.watcher = newWatcher(ac)
  71. return ac
  72. }
  73. func (ac *addrCache) accounts() []Account {
  74. ac.maybeReload()
  75. ac.mu.Lock()
  76. defer ac.mu.Unlock()
  77. cpy := make([]Account, len(ac.all))
  78. copy(cpy, ac.all)
  79. return cpy
  80. }
  81. func (ac *addrCache) hasAddress(addr common.Address) bool {
  82. ac.maybeReload()
  83. ac.mu.Lock()
  84. defer ac.mu.Unlock()
  85. return len(ac.byAddr[addr]) > 0
  86. }
  87. func (ac *addrCache) add(newAccount Account) {
  88. ac.mu.Lock()
  89. defer ac.mu.Unlock()
  90. i := sort.Search(len(ac.all), func(i int) bool { return ac.all[i].File >= newAccount.File })
  91. if i < len(ac.all) && ac.all[i] == newAccount {
  92. return
  93. }
  94. // newAccount is not in the cache.
  95. ac.all = append(ac.all, Account{})
  96. copy(ac.all[i+1:], ac.all[i:])
  97. ac.all[i] = newAccount
  98. ac.byAddr[newAccount.Address] = append(ac.byAddr[newAccount.Address], newAccount)
  99. }
  100. // note: removed needs to be unique here (i.e. both File and Address must be set).
  101. func (ac *addrCache) delete(removed Account) {
  102. ac.mu.Lock()
  103. defer ac.mu.Unlock()
  104. ac.all = removeAccount(ac.all, removed)
  105. if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
  106. delete(ac.byAddr, removed.Address)
  107. } else {
  108. ac.byAddr[removed.Address] = ba
  109. }
  110. }
  111. func removeAccount(slice []Account, elem Account) []Account {
  112. for i := range slice {
  113. if slice[i] == elem {
  114. return append(slice[:i], slice[i+1:]...)
  115. }
  116. }
  117. return slice
  118. }
  119. // find returns the cached account for address if there is a unique match.
  120. // The exact matching rules are explained by the documentation of Account.
  121. // Callers must hold ac.mu.
  122. func (ac *addrCache) find(a Account) (Account, error) {
  123. // Limit search to address candidates if possible.
  124. matches := ac.all
  125. if (a.Address != common.Address{}) {
  126. matches = ac.byAddr[a.Address]
  127. }
  128. if a.File != "" {
  129. // If only the basename is specified, complete the path.
  130. if !strings.ContainsRune(a.File, filepath.Separator) {
  131. a.File = filepath.Join(ac.keydir, a.File)
  132. }
  133. for i := range matches {
  134. if matches[i].File == a.File {
  135. return matches[i], nil
  136. }
  137. }
  138. if (a.Address == common.Address{}) {
  139. return Account{}, ErrNoMatch
  140. }
  141. }
  142. switch len(matches) {
  143. case 1:
  144. return matches[0], nil
  145. case 0:
  146. return Account{}, ErrNoMatch
  147. default:
  148. err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]Account, len(matches))}
  149. copy(err.Matches, matches)
  150. return Account{}, err
  151. }
  152. }
  153. func (ac *addrCache) maybeReload() {
  154. ac.mu.Lock()
  155. defer ac.mu.Unlock()
  156. if ac.watcher.running {
  157. return // A watcher is running and will keep the cache up-to-date.
  158. }
  159. if ac.throttle == nil {
  160. ac.throttle = time.NewTimer(0)
  161. } else {
  162. select {
  163. case <-ac.throttle.C:
  164. default:
  165. return // The cache was reloaded recently.
  166. }
  167. }
  168. ac.watcher.start()
  169. ac.reload()
  170. ac.throttle.Reset(minReloadInterval)
  171. }
  172. func (ac *addrCache) close() {
  173. ac.mu.Lock()
  174. ac.watcher.close()
  175. if ac.throttle != nil {
  176. ac.throttle.Stop()
  177. }
  178. ac.mu.Unlock()
  179. }
  180. // reload caches addresses of existing accounts.
  181. // Callers must hold ac.mu.
  182. func (ac *addrCache) reload() {
  183. accounts, err := ac.scan()
  184. if err != nil && glog.V(logger.Debug) {
  185. glog.Errorf("can't load keys: %v", err)
  186. }
  187. ac.all = accounts
  188. sort.Sort(ac.all)
  189. for k := range ac.byAddr {
  190. delete(ac.byAddr, k)
  191. }
  192. for _, a := range accounts {
  193. ac.byAddr[a.Address] = append(ac.byAddr[a.Address], a)
  194. }
  195. glog.V(logger.Debug).Infof("reloaded keys, cache has %d accounts", len(ac.all))
  196. }
  197. func (ac *addrCache) scan() ([]Account, error) {
  198. files, err := ioutil.ReadDir(ac.keydir)
  199. if err != nil {
  200. return nil, err
  201. }
  202. var (
  203. buf = new(bufio.Reader)
  204. addrs []Account
  205. keyJSON struct {
  206. Address string `json:"address"`
  207. }
  208. )
  209. for _, fi := range files {
  210. path := filepath.Join(ac.keydir, fi.Name())
  211. if skipKeyFile(fi) {
  212. glog.V(logger.Detail).Infof("ignoring file %s", path)
  213. continue
  214. }
  215. fd, err := os.Open(path)
  216. if err != nil {
  217. glog.V(logger.Detail).Infoln(err)
  218. continue
  219. }
  220. buf.Reset(fd)
  221. // Parse the address.
  222. keyJSON.Address = ""
  223. err = json.NewDecoder(buf).Decode(&keyJSON)
  224. addr := common.HexToAddress(keyJSON.Address)
  225. switch {
  226. case err != nil:
  227. glog.V(logger.Debug).Infof("can't decode key %s: %v", path, err)
  228. case (addr == common.Address{}):
  229. glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path)
  230. default:
  231. addrs = append(addrs, Account{Address: addr, File: path})
  232. }
  233. fd.Close()
  234. }
  235. return addrs, err
  236. }
  237. func skipKeyFile(fi os.FileInfo) bool {
  238. // Skip editor backups and UNIX-style hidden files.
  239. if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
  240. return true
  241. }
  242. // Skip misc special files, directories (yes, symlinks too).
  243. if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
  244. return true
  245. }
  246. return false
  247. }