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  }