github.com/xfers/quorum@v21.1.0+incompatible/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 // Quorum 140 func (am *Manager) Backend(account Account) (Backend, error) { 141 for _, b := range am.backends { 142 for _, bb := range b { 143 for _, w := range bb.Wallets() { 144 if w.Contains(account) { 145 return bb, nil 146 } 147 } 148 } 149 } 150 return nil, ErrUnknownWallet 151 } 152 153 // end Quorum 154 155 // Wallets returns all signer accounts registered under this account manager. 156 func (am *Manager) Wallets() []Wallet { 157 am.lock.RLock() 158 defer am.lock.RUnlock() 159 160 cpy := make([]Wallet, len(am.wallets)) 161 copy(cpy, am.wallets) 162 return cpy 163 } 164 165 // Wallet retrieves the wallet associated with a particular URL. 166 func (am *Manager) Wallet(url string) (Wallet, error) { 167 am.lock.RLock() 168 defer am.lock.RUnlock() 169 170 parsed, err := parseURL(url) 171 if err != nil { 172 return nil, err 173 } 174 for _, wallet := range am.Wallets() { 175 if wallet.URL() == parsed { 176 return wallet, nil 177 } 178 } 179 return nil, ErrUnknownWallet 180 } 181 182 // Accounts returns all account addresses of all wallets within the account manager 183 func (am *Manager) Accounts() []common.Address { 184 am.lock.RLock() 185 defer am.lock.RUnlock() 186 187 addresses := make([]common.Address, 0) // return [] instead of nil if empty 188 for _, wallet := range am.wallets { 189 for _, account := range wallet.Accounts() { 190 addresses = append(addresses, account.Address) 191 } 192 } 193 return addresses 194 } 195 196 // Find attempts to locate the wallet corresponding to a specific account. Since 197 // accounts can be dynamically added to and removed from wallets, this method has 198 // a linear runtime in the number of wallets. 199 func (am *Manager) Find(account Account) (Wallet, error) { 200 am.lock.RLock() 201 defer am.lock.RUnlock() 202 203 for _, wallet := range am.wallets { 204 if wallet.Contains(account) { 205 return wallet, nil 206 } 207 } 208 return nil, ErrUnknownAccount 209 } 210 211 // Subscribe creates an async subscription to receive notifications when the 212 // manager detects the arrival or departure of a wallet from any of its backends. 213 func (am *Manager) Subscribe(sink chan<- WalletEvent) event.Subscription { 214 return am.feed.Subscribe(sink) 215 } 216 217 // merge is a sorted analogue of append for wallets, where the ordering of the 218 // origin list is preserved by inserting new wallets at the correct position. 219 // 220 // The original slice is assumed to be already sorted by URL. 221 func merge(slice []Wallet, wallets ...Wallet) []Wallet { 222 for _, wallet := range wallets { 223 n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 }) 224 if n == len(slice) { 225 slice = append(slice, wallet) 226 continue 227 } 228 slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...) 229 } 230 return slice 231 } 232 233 // drop is the couterpart of merge, which looks up wallets from within the sorted 234 // cache and removes the ones specified. 235 func drop(slice []Wallet, wallets ...Wallet) []Wallet { 236 for _, wallet := range wallets { 237 n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 }) 238 if n == len(slice) { 239 // Wallet not found, may happen during startup 240 continue 241 } 242 slice = append(slice[:n], slice[n+1:]...) 243 } 244 return slice 245 }