github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/chain/accounts/manager.go (about)

     1  package accounts
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"sync"
     7  
     8  	"github.com/neatio-net/neatio/utilities/event"
     9  )
    10  
    11  type Manager struct {
    12  	backends map[reflect.Type][]Backend
    13  	updaters []event.Subscription
    14  	updates  chan WalletEvent
    15  	wallets  []Wallet
    16  
    17  	feed event.Feed
    18  
    19  	quit chan chan error
    20  	lock sync.RWMutex
    21  }
    22  
    23  func NewManager(backends ...Backend) *Manager {
    24  
    25  	var wallets []Wallet
    26  	for _, backend := range backends {
    27  		wallets = merge(wallets, backend.Wallets()...)
    28  	}
    29  
    30  	updates := make(chan WalletEvent, 4*len(backends))
    31  
    32  	subs := make([]event.Subscription, len(backends))
    33  	for i, backend := range backends {
    34  		subs[i] = backend.Subscribe(updates)
    35  	}
    36  
    37  	am := &Manager{
    38  		backends: make(map[reflect.Type][]Backend),
    39  		updaters: subs,
    40  		updates:  updates,
    41  		wallets:  wallets,
    42  		quit:     make(chan chan error),
    43  	}
    44  	for _, backend := range backends {
    45  		kind := reflect.TypeOf(backend)
    46  		am.backends[kind] = append(am.backends[kind], backend)
    47  	}
    48  	go am.update()
    49  
    50  	return am
    51  }
    52  
    53  func (am *Manager) Close() error {
    54  	errc := make(chan error)
    55  	am.quit <- errc
    56  	return <-errc
    57  }
    58  
    59  func (am *Manager) update() {
    60  
    61  	defer func() {
    62  		am.lock.Lock()
    63  		for _, sub := range am.updaters {
    64  			sub.Unsubscribe()
    65  		}
    66  		am.updaters = nil
    67  		am.lock.Unlock()
    68  	}()
    69  
    70  	for {
    71  		select {
    72  		case event := <-am.updates:
    73  
    74  			am.lock.Lock()
    75  			switch event.Kind {
    76  			case WalletArrived:
    77  				am.wallets = merge(am.wallets, event.Wallet)
    78  			case WalletDropped:
    79  				am.wallets = drop(am.wallets, event.Wallet)
    80  			}
    81  			am.lock.Unlock()
    82  
    83  			am.feed.Send(event)
    84  
    85  		case errc := <-am.quit:
    86  
    87  			errc <- nil
    88  			return
    89  		}
    90  	}
    91  }
    92  
    93  func (am *Manager) Backends(kind reflect.Type) []Backend {
    94  	return am.backends[kind]
    95  }
    96  
    97  func (am *Manager) Wallets() []Wallet {
    98  	am.lock.RLock()
    99  	defer am.lock.RUnlock()
   100  
   101  	cpy := make([]Wallet, len(am.wallets))
   102  	copy(cpy, am.wallets)
   103  	return cpy
   104  }
   105  
   106  func (am *Manager) Wallet(url string) (Wallet, error) {
   107  	am.lock.RLock()
   108  	defer am.lock.RUnlock()
   109  
   110  	parsed, err := parseURL(url)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	for _, wallet := range am.Wallets() {
   115  		if wallet.URL() == parsed {
   116  			return wallet, nil
   117  		}
   118  	}
   119  	return nil, ErrUnknownWallet
   120  }
   121  
   122  func (am *Manager) Find(account Account) (Wallet, error) {
   123  	am.lock.RLock()
   124  	defer am.lock.RUnlock()
   125  
   126  	for _, wallet := range am.wallets {
   127  		if wallet.Contains(account) {
   128  			return wallet, nil
   129  		}
   130  	}
   131  	return nil, ErrUnknownAccount
   132  }
   133  
   134  func (am *Manager) Subscribe(sink chan<- WalletEvent) event.Subscription {
   135  	return am.feed.Subscribe(sink)
   136  }
   137  
   138  func merge(slice []Wallet, wallets ...Wallet) []Wallet {
   139  	for _, wallet := range wallets {
   140  		n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
   141  		if n == len(slice) {
   142  			slice = append(slice, wallet)
   143  			continue
   144  		}
   145  		slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...)
   146  	}
   147  	return slice
   148  }
   149  
   150  func drop(slice []Wallet, wallets ...Wallet) []Wallet {
   151  	for _, wallet := range wallets {
   152  		n := sort.Search(len(slice), func(i int) bool { return slice[i].URL().Cmp(wallet.URL()) >= 0 })
   153  		if n == len(slice) {
   154  
   155  			continue
   156  		}
   157  		slice = append(slice[:n], slice[n+1:]...)
   158  	}
   159  	return slice
   160  }