github.com/MetalBlockchain/subnet-evm@v0.4.9/accounts/manager.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2017 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package accounts 28 29 import ( 30 "reflect" 31 "sort" 32 "sync" 33 34 "github.com/ethereum/go-ethereum/common" 35 "github.com/ethereum/go-ethereum/event" 36 ) 37 38 // managerSubBufferSize determines how many incoming wallet events 39 // the manager will buffer in its channel. 40 const managerSubBufferSize = 50 41 42 // Config contains the settings of the global account manager. 43 // 44 // TODO(rjl493456442, karalabe, holiman): Get rid of this when account management 45 // is removed in favor of Clef. 46 type Config struct { 47 InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed 48 } 49 50 // newBackendEvent lets the manager know it should 51 // track the given backend for wallet updates. 52 type newBackendEvent struct { 53 backend Backend 54 processed chan struct{} // Informs event emitter that backend has been integrated 55 } 56 57 // Manager is an overarching account manager that can communicate with various 58 // backends for signing transactions. 59 type Manager struct { 60 config *Config // Global account manager configurations 61 backends map[reflect.Type][]Backend // Index of backends currently registered 62 updaters []event.Subscription // Wallet update subscriptions for all backends 63 updates chan WalletEvent // Subscription sink for backend wallet changes 64 newBackends chan newBackendEvent // Incoming backends to be tracked by the manager 65 wallets []Wallet // Cache of all wallets from all registered backends 66 67 feed event.Feed // Wallet feed notifying of arrivals/departures 68 69 quit chan chan error 70 term chan struct{} // Channel is closed upon termination of the update loop 71 lock sync.RWMutex 72 } 73 74 // NewManager creates a generic account manager to sign transaction via various 75 // supported backends. 76 func NewManager(config *Config, backends ...Backend) *Manager { 77 // Retrieve the initial list of wallets from the backends and sort by URL 78 var wallets []Wallet 79 for _, backend := range backends { 80 wallets = merge(wallets, backend.Wallets()...) 81 } 82 // Subscribe to wallet notifications from all backends 83 updates := make(chan WalletEvent, managerSubBufferSize) 84 85 subs := make([]event.Subscription, len(backends)) 86 for i, backend := range backends { 87 subs[i] = backend.Subscribe(updates) 88 } 89 // Assemble the account manager and return 90 am := &Manager{ 91 config: config, 92 backends: make(map[reflect.Type][]Backend), 93 updaters: subs, 94 updates: updates, 95 newBackends: make(chan newBackendEvent), 96 wallets: wallets, 97 quit: make(chan chan error), 98 term: make(chan struct{}), 99 } 100 for _, backend := range backends { 101 kind := reflect.TypeOf(backend) 102 am.backends[kind] = append(am.backends[kind], backend) 103 } 104 go am.update() 105 106 return am 107 } 108 109 // Close terminates the account manager's internal notification processes. 110 func (am *Manager) Close() error { 111 errc := make(chan error) 112 am.quit <- errc 113 return <-errc 114 } 115 116 // Config returns the configuration of account manager. 117 func (am *Manager) Config() *Config { 118 return am.config 119 } 120 121 // AddBackend starts the tracking of an additional backend for wallet updates. 122 // cmd/geth assumes once this func returns the backends have been already integrated. 123 func (am *Manager) AddBackend(backend Backend) { 124 done := make(chan struct{}) 125 am.newBackends <- newBackendEvent{backend, done} 126 <-done 127 } 128 129 // update is the wallet event loop listening for notifications from the backends 130 // and updating the cache of wallets. 131 func (am *Manager) update() { 132 // Close all subscriptions when the manager terminates 133 defer func() { 134 am.lock.Lock() 135 for _, sub := range am.updaters { 136 sub.Unsubscribe() 137 } 138 am.updaters = nil 139 am.lock.Unlock() 140 }() 141 142 // Loop until termination 143 for { 144 select { 145 case event := <-am.updates: 146 // Wallet event arrived, update local cache 147 am.lock.Lock() 148 switch event.Kind { 149 case WalletArrived: 150 am.wallets = merge(am.wallets, event.Wallet) 151 case WalletDropped: 152 am.wallets = drop(am.wallets, event.Wallet) 153 } 154 am.lock.Unlock() 155 156 // Notify any listeners of the event 157 am.feed.Send(event) 158 case event := <-am.newBackends: 159 am.lock.Lock() 160 // Update caches 161 backend := event.backend 162 am.wallets = merge(am.wallets, backend.Wallets()...) 163 am.updaters = append(am.updaters, backend.Subscribe(am.updates)) 164 kind := reflect.TypeOf(backend) 165 am.backends[kind] = append(am.backends[kind], backend) 166 am.lock.Unlock() 167 close(event.processed) 168 case errc := <-am.quit: 169 // Manager terminating, return 170 errc <- nil 171 // Signals event emitters the loop is not receiving values 172 // to prevent them from getting stuck. 173 close(am.term) 174 return 175 } 176 } 177 } 178 179 // Backends retrieves the backend(s) with the given type from the account manager. 180 func (am *Manager) Backends(kind reflect.Type) []Backend { 181 am.lock.RLock() 182 defer am.lock.RUnlock() 183 184 return am.backends[kind] 185 } 186 187 // Wallets returns all signer accounts registered under this account manager. 188 func (am *Manager) Wallets() []Wallet { 189 am.lock.RLock() 190 defer am.lock.RUnlock() 191 192 return am.walletsNoLock() 193 } 194 195 // walletsNoLock returns all registered wallets. Callers must hold am.lock. 196 func (am *Manager) walletsNoLock() []Wallet { 197 cpy := make([]Wallet, len(am.wallets)) 198 copy(cpy, am.wallets) 199 return cpy 200 } 201 202 // Wallet retrieves the wallet associated with a particular URL. 203 func (am *Manager) Wallet(url string) (Wallet, error) { 204 am.lock.RLock() 205 defer am.lock.RUnlock() 206 207 parsed, err := parseURL(url) 208 if err != nil { 209 return nil, err 210 } 211 for _, wallet := range am.walletsNoLock() { 212 if wallet.URL() == parsed { 213 return wallet, nil 214 } 215 } 216 return nil, ErrUnknownWallet 217 } 218 219 // Accounts returns all account addresses of all wallets within the account manager 220 func (am *Manager) Accounts() []common.Address { 221 am.lock.RLock() 222 defer am.lock.RUnlock() 223 224 addresses := make([]common.Address, 0) // return [] instead of nil if empty 225 for _, wallet := range am.wallets { 226 for _, account := range wallet.Accounts() { 227 addresses = append(addresses, account.Address) 228 } 229 } 230 return addresses 231 } 232 233 // Find attempts to locate the wallet corresponding to a specific account. Since 234 // accounts can be dynamically added to and removed from wallets, this method has 235 // a linear runtime in the number of wallets. 236 func (am *Manager) Find(account Account) (Wallet, error) { 237 am.lock.RLock() 238 defer am.lock.RUnlock() 239 240 for _, wallet := range am.wallets { 241 if wallet.Contains(account) { 242 return wallet, nil 243 } 244 } 245 return nil, ErrUnknownAccount 246 } 247 248 // Subscribe creates an async subscription to receive notifications when the 249 // manager detects the arrival or departure of a wallet from any of its backends. 250 func (am *Manager) Subscribe(sink chan<- WalletEvent) event.Subscription { 251 return am.feed.Subscribe(sink) 252 } 253 254 // merge is a sorted analogue of append for wallets, where the ordering of the 255 // origin list is preserved by inserting new wallets at the correct position. 256 // 257 // The original slice is assumed to be already sorted by URL. 258 func merge(slice []Wallet, wallets ...Wallet) []Wallet { 259 for _, wallet := range wallets { 260 n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 }) 261 if n == len(slice) { 262 slice = append(slice, wallet) 263 continue 264 } 265 slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...) 266 } 267 return slice 268 } 269 270 // drop is the couterpart of merge, which looks up wallets from within the sorted 271 // cache and removes the ones specified. 272 func drop(slice []Wallet, wallets ...Wallet) []Wallet { 273 for _, wallet := range wallets { 274 n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 }) 275 if n == len(slice) { 276 // Wallet not found, may happen during startup 277 continue 278 } 279 slice = append(slice[:n], slice[n+1:]...) 280 } 281 return slice 282 }