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