| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | // Copyright 2018 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // This package implements support for smartcard-based hardware wallets such as | 
					
						
							|  |  |  | // the one written by Status: https://github.com/status-im/hardware-wallet | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This implementation of smartcard wallets have a different interaction process | 
					
						
							|  |  |  | // to other types of hardware wallet. The process works like this: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // 1. (First use with a given client) Establish a pairing between hardware | 
					
						
							| 
									
										
										
										
											2019-03-20 19:09:47 +01:00
										 |  |  | //    wallet and client. This requires a secret value called a 'pairing password'. | 
					
						
							|  |  |  | //    You can pair with an unpaired wallet with `personal.openWallet(URI, pairing password)`. | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // 2. (First use only) Initialize the wallet, which generates a keypair, stores | 
					
						
							|  |  |  | //    it on the wallet, and returns it so the user can back it up. You can | 
					
						
							|  |  |  | //    initialize a wallet with `personal.initializeWallet(URI)`. | 
					
						
							|  |  |  | // 3. Connect to the wallet using the pairing information established in step 1. | 
					
						
							|  |  |  | //    You can connect to a paired wallet with `personal.openWallet(URI, PIN)`. | 
					
						
							|  |  |  | // 4. Interact with the wallet as normal. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | package scwallet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2019-04-04 20:06:40 +02:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/accounts" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/event" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/log" | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 	pcsc "github.com/gballet/go-libpcsclite" | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // Scheme is the URI prefix for smartcard wallets. | 
					
						
							| 
									
										
										
										
											2019-05-28 18:47:53 +02:00
										 |  |  | const Scheme = "keycard" | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // refreshCycle is the maximum time between wallet refreshes (if USB hotplug | 
					
						
							|  |  |  | // notifications don't work). | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | const refreshCycle = time.Second | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // refreshThrottling is the minimum time between wallet refreshes to avoid thrashing. | 
					
						
							|  |  |  | const refreshThrottling = 500 * time.Millisecond | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // smartcardPairing contains information about a smart card we have paired with | 
					
						
							|  |  |  | // or might pair with the hub. | 
					
						
							|  |  |  | type smartcardPairing struct { | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	PublicKey    []byte                                     `json:"publicKey"` | 
					
						
							|  |  |  | 	PairingIndex uint8                                      `json:"pairingIndex"` | 
					
						
							|  |  |  | 	PairingKey   []byte                                     `json:"pairingKey"` | 
					
						
							|  |  |  | 	Accounts     map[common.Address]accounts.DerivationPath `json:"accounts"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Hub is a accounts.Backend that can find and handle generic PC/SC hardware wallets. | 
					
						
							|  |  |  | type Hub struct { | 
					
						
							|  |  |  | 	scheme string // Protocol scheme prefixing account and wallet URLs. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 	context  *pcsc.Client | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 	datadir  string | 
					
						
							|  |  |  | 	pairings map[string]smartcardPairing | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	refreshed   time.Time               // Time instance when the list of wallets was last refreshed | 
					
						
							|  |  |  | 	wallets     map[string]*Wallet      // Mapping from reader names to wallet instances | 
					
						
							|  |  |  | 	updateFeed  event.Feed              // Event feed to notify wallet additions/removals | 
					
						
							|  |  |  | 	updateScope event.SubscriptionScope // Subscription scope tracking current live listeners | 
					
						
							|  |  |  | 	updating    bool                    // Whether the event notification loop is running | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	quit chan chan error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 	stateLock sync.RWMutex // Protects the internals of the hub from racey access | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (hub *Hub) readPairings() error { | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	hub.pairings = make(map[string]smartcardPairing) | 
					
						
							| 
									
										
										
										
											2019-04-04 09:19:20 +02:00
										 |  |  | 	pairingFile, err := os.Open(filepath.Join(hub.datadir, "smartcards.json")) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(err) { | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pairingData, err := ioutil.ReadAll(pairingFile) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	var pairings []smartcardPairing | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if err := json.Unmarshal(pairingData, &pairings); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, pairing := range pairings { | 
					
						
							|  |  |  | 		hub.pairings[string(pairing.PublicKey)] = pairing | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (hub *Hub) writePairings() error { | 
					
						
							| 
									
										
										
										
											2019-04-04 20:06:40 +02:00
										 |  |  | 	pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE, 0755) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-04 20:06:40 +02:00
										 |  |  | 	defer pairingFile.Close() | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | 	pairings := make([]smartcardPairing, 0, len(hub.pairings)) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	for _, pairing := range hub.pairings { | 
					
						
							|  |  |  | 		pairings = append(pairings, pairing) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pairingData, err := json.Marshal(pairings) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err := pairingFile.Write(pairingData); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-04 20:06:40 +02:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing { | 
					
						
							| 
									
										
										
										
											2019-04-04 20:06:40 +02:00
										 |  |  | 	if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok { | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		return &pairing | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error { | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if pairing == nil { | 
					
						
							|  |  |  | 		delete(hub.pairings, string(wallet.PublicKey)) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		hub.pairings[string(wallet.PublicKey)] = *pairing | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return hub.writePairings() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewHub creates a new hardware wallet manager for smartcards. | 
					
						
							| 
									
										
										
										
											2019-05-31 11:30:28 +02:00
										 |  |  | func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) { | 
					
						
							|  |  |  | 	context, err := pcsc.EstablishContext(daemonPath, pcsc.ScopeSystem) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	hub := &Hub{ | 
					
						
							|  |  |  | 		scheme:  scheme, | 
					
						
							|  |  |  | 		context: context, | 
					
						
							|  |  |  | 		datadir: datadir, | 
					
						
							|  |  |  | 		wallets: make(map[string]*Wallet), | 
					
						
							|  |  |  | 		quit:    make(chan chan error), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := hub.readPairings(); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	hub.refreshWallets() | 
					
						
							|  |  |  | 	return hub, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | // Wallets implements accounts.Backend, returning all the currently tracked smart | 
					
						
							|  |  |  | // cards that appear to be hardware wallets. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | func (hub *Hub) Wallets() []accounts.Wallet { | 
					
						
							|  |  |  | 	// Make sure the list of wallets is up to date | 
					
						
							|  |  |  | 	hub.refreshWallets() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 	hub.stateLock.RLock() | 
					
						
							|  |  |  | 	defer hub.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	cpy := make([]accounts.Wallet, 0, len(hub.wallets)) | 
					
						
							|  |  |  | 	for _, wallet := range hub.wallets { | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 		cpy = append(cpy, wallet) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-20 10:37:45 +03:00
										 |  |  | 	sort.Sort(accounts.WalletsByURL(cpy)) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	return cpy | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-22 13:27:41 +00:00
										 |  |  | // refreshWallets scans the devices attached to the machine and updates the | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | // list of wallets based on the found devices. | 
					
						
							|  |  |  | func (hub *Hub) refreshWallets() { | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 	// Don't scan the USB like crazy it the user fetches wallets in a loop | 
					
						
							|  |  |  | 	hub.stateLock.RLock() | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	elapsed := time.Since(hub.refreshed) | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 	hub.stateLock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	if elapsed < refreshThrottling { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 	// Retrieve all the smart card reader to check for cards | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	readers, err := hub.context.ListReaders() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 		// This is a perverted hack, the scard library returns an error if no card | 
					
						
							|  |  |  | 		// readers are present instead of simply returning an empty list. We don't | 
					
						
							|  |  |  | 		// want to fill the user's log with errors, so filter those out. | 
					
						
							|  |  |  | 		if err.Error() != "scard: Cannot find a smart card reader." { | 
					
						
							|  |  |  | 			log.Error("Failed to enumerate smart card readers", "err", err) | 
					
						
							| 
									
										
										
										
											2019-04-04 20:06:40 +02:00
										 |  |  | 			return | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 	// Transform the current list of wallets into the new one | 
					
						
							|  |  |  | 	hub.stateLock.Lock() | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	events := []accounts.WalletEvent{} | 
					
						
							|  |  |  | 	seen := make(map[string]struct{}) | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 	for _, reader := range readers { | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 		// Mark the reader as present | 
					
						
							|  |  |  | 		seen[reader] = struct{}{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-25 16:21:28 +08:00
										 |  |  | 		// If we already know about this card, skip to the next reader, otherwise clean up | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		if wallet, ok := hub.wallets[reader]; ok { | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 			if err := wallet.ping(); err == nil { | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 			wallet.Close() | 
					
						
							|  |  |  | 			events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) | 
					
						
							|  |  |  | 			delete(hub.wallets, reader) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 		// New card detected, try to connect to it | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 		card, err := hub.context.Connect(reader, pcsc.ShareShared, pcsc.ProtocolAny) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 			log.Debug("Failed to open smart card", "reader", reader, "err", err) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		wallet := NewWallet(hub, card) | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 		if err = wallet.connect(); err != nil { | 
					
						
							|  |  |  | 			log.Debug("Failed to connect to smart card", "reader", reader, "err", err) | 
					
						
							| 
									
										
										
										
											2019-03-14 23:03:13 +01:00
										 |  |  | 			card.Disconnect(pcsc.LeaveCard) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 		// Card connected, start tracking in amongs the wallets | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		hub.wallets[reader] = wallet | 
					
						
							|  |  |  | 		events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived}) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 	// Remove any wallets no longer present | 
					
						
							|  |  |  | 	for reader, wallet := range hub.wallets { | 
					
						
							|  |  |  | 		if _, ok := seen[reader]; !ok { | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 			wallet.Close() | 
					
						
							|  |  |  | 			events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 			delete(hub.wallets, reader) | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 	hub.refreshed = time.Now() | 
					
						
							|  |  |  | 	hub.stateLock.Unlock() | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, event := range events { | 
					
						
							|  |  |  | 		hub.updateFeed.Send(event) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Subscribe implements accounts.Backend, creating an async subscription to | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | // receive notifications on the addition or removal of smart card wallets. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription { | 
					
						
							|  |  |  | 	// We need the mutex to reliably start/stop the update loop | 
					
						
							|  |  |  | 	hub.stateLock.Lock() | 
					
						
							|  |  |  | 	defer hub.stateLock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Subscribe the caller and track the subscriber count | 
					
						
							|  |  |  | 	sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Subscribers require an active notification loop, start it | 
					
						
							|  |  |  | 	if !hub.updating { | 
					
						
							|  |  |  | 		hub.updating = true | 
					
						
							|  |  |  | 		go hub.updater() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sub | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // updater is responsible for maintaining an up-to-date list of wallets managed | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | // by the smart card hub, and for firing wallet addition/removal events. | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | func (hub *Hub) updater() { | 
					
						
							|  |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 		// TODO: Wait for a USB hotplug event (not supported yet) or a refresh timeout | 
					
						
							|  |  |  | 		// <-hub.changes | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		time.Sleep(refreshCycle) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Run the wallet refresher | 
					
						
							|  |  |  | 		hub.refreshWallets() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// If all our subscribers left, stop the updater | 
					
						
							| 
									
										
										
										
											2018-04-19 13:39:33 +03:00
										 |  |  | 		hub.stateLock.Lock() | 
					
						
							| 
									
										
										
										
											2018-01-07 18:38:11 +00:00
										 |  |  | 		if hub.updateScope.Count() == 0 { | 
					
						
							|  |  |  | 			hub.updating = false | 
					
						
							|  |  |  | 			hub.stateLock.Unlock() | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		hub.stateLock.Unlock() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |