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