manager.go 6.9 KB

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