manager.go 7.2 KB

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