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