github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/accounts/usbwallet/hub.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:27</date>
    10  //</624342586424823808>
    11  
    12  
    13  package usbwallet
    14  
    15  import (
    16  	"errors"
    17  	"runtime"
    18  	"sync"
    19  	"time"
    20  
    21  	"github.com/ethereum/go-ethereum/accounts"
    22  	"github.com/ethereum/go-ethereum/event"
    23  	"github.com/ethereum/go-ethereum/log"
    24  	"github.com/karalabe/hid"
    25  )
    26  
    27  //Ledgerscheme是协议方案的前缀帐户和钱包URL。
    28  const LedgerScheme = "ledger"
    29  
    30  //Trezorscheme是协议方案的前缀帐户和钱包URL。
    31  const TrezorScheme = "trezor"
    32  
    33  //刷新周期是钱包刷新之间的最长时间(如果是USB热插拔
    34  //通知不起作用)。
    35  const refreshCycle = time.Second
    36  
    37  //RefreshThrottling是钱包刷新之间避免USB的最短时间间隔。
    38  //蹂躏
    39  const refreshThrottling = 500 * time.Millisecond
    40  
    41  //Hub是一个帐户。后端可以查找和处理通用的USB硬件钱包。
    42  type Hub struct {
    43  scheme     string                  //协议方案前缀帐户和钱包URL。
    44  vendorID   uint16                  //用于设备发现的USB供应商标识符
    45  productIDs []uint16                //用于设备发现的USB产品标识符
    46  usageID    uint16                  //用于MacOS设备发现的USB使用页标识符
    47  endpointID int                     //用于非MacOS设备发现的USB端点标识符
    48  makeDriver func(log.Logger) driver //构造供应商特定驱动程序的工厂方法
    49  
    50  refreshed   time.Time               //上次刷新钱包列表时的时间实例
    51  wallets     []accounts.Wallet       //当前跟踪的USB钱包设备列表
    52  updateFeed  event.Feed              //通知钱包添加/删除的事件源
    53  updateScope event.SubscriptionScope //订阅范围跟踪当前实时侦听器
    54  updating    bool                    //事件通知循环是否正在运行
    55  
    56  	quit chan chan error
    57  
    58  stateLock sync.RWMutex //保护轮毂内部不受滚道的影响
    59  
    60  //TODO(karalabe):如果热插拔落在Windows上,则移除。
    61  commsPend int        //阻止枚举的操作数
    62  commsLock sync.Mutex //保护挂起计数器和枚举的锁
    63  }
    64  
    65  //newledgerhub为分类帐设备创建了一个新的硬件钱包管理器。
    66  func NewLedgerHub() (*Hub, error) {
    67   /*urn newhub(Ledgerscheme,0x2c97,[]uint16 0x0000/*Ledger Blue*/,0x001/*Ledger Nano S*/,0xffa0,0,NewledgerDriver)
    68  }
    69  
    70  //newtrezorhub为trezor设备创建新的硬件钱包管理器。
    71  func newtrezorhub()(*hub,错误)
    72   返回newhub(trezorscheme,0x534c,[]uint16 0x001/*trezor 1*/}, 0xff00, 0, newTrezorDriver)
    73  
    74  }
    75  
    76  //NewHub为通用USB设备创建了一个新的硬件钱包管理器。
    77  func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) {
    78  	if !hid.Supported() {
    79  		return nil, errors.New("unsupported platform")
    80  	}
    81  	hub := &Hub{
    82  		scheme:     scheme,
    83  		vendorID:   vendorID,
    84  		productIDs: productIDs,
    85  		usageID:    usageID,
    86  		endpointID: endpointID,
    87  		makeDriver: makeDriver,
    88  		quit:       make(chan chan error),
    89  	}
    90  	hub.refreshWallets()
    91  	return hub, nil
    92  }
    93  
    94  //钱包实现帐户。后端,返回当前跟踪的所有USB
    95  //看起来像是硬件钱包的设备。
    96  func (hub *Hub) Wallets() []accounts.Wallet {
    97  //确保钱包列表是最新的
    98  	hub.refreshWallets()
    99  
   100  	hub.stateLock.RLock()
   101  	defer hub.stateLock.RUnlock()
   102  
   103  	cpy := make([]accounts.Wallet, len(hub.wallets))
   104  	copy(cpy, hub.wallets)
   105  	return cpy
   106  }
   107  
   108  //刷新钱包扫描连接到机器的USB设备并更新
   109  //基于找到的设备的钱包列表。
   110  func (hub *Hub) refreshWallets() {
   111  //不要像疯了一样扫描USB,用户会在一个循环中取出钱包。
   112  	hub.stateLock.RLock()
   113  	elapsed := time.Since(hub.refreshed)
   114  	hub.stateLock.RUnlock()
   115  
   116  	if elapsed < refreshThrottling {
   117  		return
   118  	}
   119  //检索USB钱包设备的当前列表
   120  	var devices []hid.DeviceInfo
   121  
   122  	if runtime.GOOS == "linux" {
   123  //Linux上的hidapi在枚举期间打开设备以检索一些信息,
   124  //如果正在等待用户确认,则破坏分类帐协议。这个
   125  //在分类帐上确认了错误,但不会在旧设备上修复,所以我们
   126  //需要自己防止并发通信。更优雅的解决方案是
   127  //放弃枚举以支持热插拔事件,但这还不起作用
   128  //在Windows上,所以如果我们无论如何都需要破解它,现在这就更优雅了。
   129  		hub.commsLock.Lock()
   130  if hub.commsPend > 0 { //正在等待确认,不刷新
   131  			hub.commsLock.Unlock()
   132  			return
   133  		}
   134  	}
   135  	for _, info := range hid.Enumerate(hub.vendorID, 0) {
   136  		for _, id := range hub.productIDs {
   137  			if info.ProductID == id && (info.UsagePage == hub.usageID || info.Interface == hub.endpointID) {
   138  				devices = append(devices, info)
   139  				break
   140  			}
   141  		}
   142  	}
   143  	if runtime.GOOS == "linux" {
   144  //请参阅枚举前的基本原理,了解为什么只在Linux上需要这样做。
   145  		hub.commsLock.Unlock()
   146  	}
   147  //将当前钱包列表转换为新列表
   148  	hub.stateLock.Lock()
   149  
   150  	wallets := make([]accounts.Wallet, 0, len(devices))
   151  	events := []accounts.WalletEvent{}
   152  
   153  	for _, device := range devices {
   154  		url := accounts.URL{Scheme: hub.scheme, Path: device.Path}
   155  
   156  //将钱包放在下一个设备或因某种原因失败的设备前面
   157  		for len(hub.wallets) > 0 {
   158  //如果我们超过当前设备并找到可操作的设备,则中止
   159  			_, failure := hub.wallets[0].Status()
   160  			if hub.wallets[0].URL().Cmp(url) >= 0 || failure == nil {
   161  				break
   162  			}
   163  //丢弃陈旧和故障的设备
   164  			events = append(events, accounts.WalletEvent{Wallet: hub.wallets[0], Kind: accounts.WalletDropped})
   165  			hub.wallets = hub.wallets[1:]
   166  		}
   167  //如果没有更多钱包或设备在下一个之前,请包装新钱包。
   168  		if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 {
   169  			logger := log.New("url", url)
   170  			wallet := &wallet{hub: hub, driver: hub.makeDriver(logger), url: &url, info: device, log: logger}
   171  
   172  			events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
   173  			wallets = append(wallets, wallet)
   174  			continue
   175  		}
   176  //如果设备与第一个钱包相同,请保留它
   177  		if hub.wallets[0].URL().Cmp(url) == 0 {
   178  			wallets = append(wallets, hub.wallets[0])
   179  			hub.wallets = hub.wallets[1:]
   180  			continue
   181  		}
   182  	}
   183  //扔掉所有剩余的钱包,并设置新的一批
   184  	for _, wallet := range hub.wallets {
   185  		events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
   186  	}
   187  	hub.refreshed = time.Now()
   188  	hub.wallets = wallets
   189  	hub.stateLock.Unlock()
   190  
   191  //启动所有钱包事件并返回
   192  	for _, event := range events {
   193  		hub.updateFeed.Send(event)
   194  	}
   195  }
   196  
   197  //subscribe实现accounts.backend,创建对的异步订阅
   198  //接收有关添加或删除USB钱包的通知。
   199  func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
   200  //我们需要mutex来可靠地启动/停止更新循环
   201  	hub.stateLock.Lock()
   202  	defer hub.stateLock.Unlock()
   203  
   204  //订阅调用方并跟踪订阅方计数
   205  	sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink))
   206  
   207  //订阅服务器需要一个活动的通知循环,启动它
   208  	if !hub.updating {
   209  		hub.updating = true
   210  		go hub.updater()
   211  	}
   212  	return sub
   213  }
   214  
   215  //更新程序负责维护管理的钱包的最新列表
   216  //通过USB集线器启动钱包添加/删除事件。
   217  func (hub *Hub) updater() {
   218  	for {
   219  //TODO:等待USB热插拔事件(尚不支持)或刷新超时
   220  //<Hub变化
   221  		time.Sleep(refreshCycle)
   222  
   223  //运行钱包刷新程序
   224  		hub.refreshWallets()
   225  
   226  //如果我们所有的订户都离开了,请停止更新程序
   227  		hub.stateLock.Lock()
   228  		if hub.updateScope.Count() == 0 {
   229  			hub.updating = false
   230  			hub.stateLock.Unlock()
   231  			return
   232  		}
   233  		hub.stateLock.Unlock()
   234  	}
   235  }
   236