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