github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/accounts/usbwallet/hub.go (about)

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