github.com/skoak/go-ethereum@v1.9.7/accounts/manager.go (about) 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 17 package accounts 18 19 import ( 20 "reflect" 21 "sort" 22 "sync" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/event" 26 ) 27 28 // Config contains the settings of the global account manager. 29 // 30 // TODO(rjl493456442, karalabe, holiman): Get rid of this when account management 31 // is removed in favor of Clef. 32 type Config struct { 33 InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed 34 } 35 36 // Manager is an overarching account manager that can communicate with various 37 // backends for signing transactions. 38 type Manager struct { 39 config *Config // Global account manager configurations 40 backends map[reflect.Type][]Backend // Index of backends currently registered 41 updaters []event.Subscription // Wallet update subscriptions for all backends 42 updates chan WalletEvent // Subscription sink for backend wallet changes 43 wallets []Wallet // Cache of all wallets from all registered backends 44 45 feed event.Feed // Wallet feed notifying of arrivals/departures 46 47 quit chan chan error 48 lock sync.RWMutex 49 } 50 51 // NewManager creates a generic account manager to sign transaction via various 52 // supported backends. 53 func NewManager(config *Config, backends ...Backend) *Manager { 54 // Retrieve the initial list of wallets from the backends and sort by URL 55 var wallets []Wallet 56 for _, backend := range backends { 57 wallets = merge(wallets, backend.Wallets()...) 58 } 59 // Subscribe to wallet notifications from all backends 60 updates := make(chan WalletEvent, 4*len(backends)) 61 62 subs := make([]event.Subscription, len(backends)) 63 for i, backend := range backends { 64 subs[i] = backend.Subscribe(updates) 65 } 66 // Assemble the account manager and return 67 am := &Manager{ 68 config: config, 69 backends: make(map[reflect.Type][]Backend), 70 updaters: subs, 71 updates: updates, 72 wallets: wallets, 73 quit: make(chan chan error), 74 } 75 for _, backend := range backends { 76 kind := reflect.TypeOf(backend) 77 am.backends[kind] = append(am.backends[kind], backend) 78 } 79 go am.update() 80 81 return am 82 } 83 84 // Close terminates the account manager's internal notification processes. 85 func (am *Manager) Close() error { 86 errc := make(chan error) 87 am.quit <- errc 88 return <-errc 89 } 90 91 // Config returns the configuration of account manager. 92 func (am *Manager) Config() *Config { 93 return am.config 94 } 95 96 // update is the wallet event loop listening for notifications from the backends 97 // and updating the cache of wallets. 98 func (am *Manager) update() { 99 // Close all subscriptions when the manager terminates 100 defer func() { 101 am.lock.Lock() 102 for _, sub := range am.updaters { 103 sub.Unsubscribe() 104 } 105 am.updaters = nil 106 am.lock.Unlock() 107 }() 108 109 // Loop until termination 110 for { 111 select { 112 case event := <-am.updates: 113 // Wallet event arrived, update local cache 114 am.lock.Lock() 115 switch event.Kind { 116 case WalletArrived: 117 am.wallets = merge(am.wallets, event.Wallet) 118 case WalletDropped: 119 am.wallets = drop(am.wallets, event.Wallet) 120 } 121 am.lock.Unlock() 122 123 // Notify any listeners of the event 124 am.feed.Send(event) 125 126 case errc := <-am.quit: 127 // Manager terminating, return 128 errc <- nil 129 return 130 } 131 } 132 } 133 134 // Backends retrieves the backend(s) with the given type from the account manager. 135 func (am *Manager) Backends(kind reflect.Type) []Backend { 136 return am.backends[kind] 137 } 138 139 // Wallets returns all signer accounts registered under this account manager. 140 func (am *Manager) Wallets() []Wallet { 141 am.lock.RLock() 142 defer am.lock.RUnlock() 143 144 cpy := make([]Wallet, len(am.wallets)) 145 copy(cpy, am.wallets) 146 return cpy 147 } 148 149 // Wallet retrieves the wallet associated with a particular URL. 150 func (am *Manager) Wallet(url string) (Wallet, error) { 151 am.lock.RLock() 152 defer am.lock.RUnlock() 153 154 parsed, err := parseURL(url) 155 if err != nil { 156 return nil, err 157 } 158 for _, wallet := range am.Wallets() { 159 if wallet.URL() == parsed { 160 return wallet, nil 161 } 162 } 163 return nil, ErrUnknownWallet 164 } 165 166 // Accounts returns all account addresses of all wallets within the account manager 167 func (am *Manager) Accounts() []common.Address { 168 am.lock.RLock() 169 defer am.lock.RUnlock() 170 171 addresses := make([]common.Address, 0) // return [] instead of nil if empty 172 for _, wallet := range am.wallets { 173 for _, account := range wallet.Accounts() { 174 addresses = append(addresses, account.Address) 175 } 176 } 177 return addresses 178 } 179 180 // Find attempts to locate the wallet corresponding to a specific account. Since 181 // accounts can be dynamically added to and removed from wallets, this method has 182 // a linear runtime in the number of wallets. 183 func (am *Manager) Find(account Account) (Wallet, error) { 184 am.lock.RLock() 185 defer am.lock.RUnlock() 186 187 for _, wallet := range am.wallets { 188 if wallet.Contains(account) { 189 return wallet, nil 190 } 191 } 192 return nil, ErrUnknownAccount 193 } 194 195 // Subscribe creates an async subscription to receive notifications when the 196 // manager detects the arrival or departure of a wallet from any of its backends. 197 func (am *Manager) Subscribe(sink chan<- WalletEvent) event.Subscription { 198 return am.feed.Subscribe(sink) 199 } 200 201 // merge is a sorted analogue of append for wallets, where the ordering of the 202 // origin list is preserved by inserting new wallets at the correct position. 203 // 204 // The original slice is assumed to be already sorted by URL. 205 func merge(slice []Wallet, wallets ...Wallet) []Wallet { 206 for _, wallet := range wallets { 207 n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 }) 208 if n == len(slice) { 209 slice = append(slice, wallet) 210 continue 211 } 212 slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...) 213 } 214 return slice 215 } 216 217 // drop is the couterpart of merge, which looks up wallets from within the sorted 218 // cache and removes the ones specified. 219 func drop(slice []Wallet, wallets ...Wallet) []Wallet { 220 for _, wallet := range wallets { 221 n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 }) 222 if n == len(slice) { 223 // Wallet not found, may happen during startup 224 continue 225 } 226 slice = append(slice[:n], slice[n+1:]...) 227 } 228 return slice 229 }