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