github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/accounts/usbwallet/wallet.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 //</624342587196575744> 11 12 13 //软件包usbwallet实现对USB硬件钱包的支持。 14 package usbwallet 15 16 import ( 17 "context" 18 "fmt" 19 "io" 20 "math/big" 21 "sync" 22 "time" 23 24 ethereum "github.com/ethereum/go-ethereum" 25 "github.com/ethereum/go-ethereum/accounts" 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core/types" 28 "github.com/ethereum/go-ethereum/log" 29 "github.com/karalabe/hid" 30 ) 31 32 //钱包健康检查之间检测USB拔出的最长时间。 33 const heartbeatCycle = time.Second 34 35 //在自派生尝试之间等待的最短时间,即使用户 36 //像疯了一样申请账户。 37 const selfDeriveThrottling = time.Second 38 39 //驱动程序定义特定于供应商的功能硬件钱包实例 40 //必须实施以允许在钱包生命周期管理中使用它们。 41 type driver interface { 42 //状态返回文本状态以帮助用户处于 43 //钱包。它还返回一个错误,指示钱包可能发生的任何故障。 44 //遇到。 45 Status() (string, error) 46 47 //open初始化对钱包实例的访问。passphrase参数可以 48 //或者不可用于特定钱包实例的实现。 49 Open(device io.ReadWriter, passphrase string) error 50 51 //关闭释放打开钱包实例持有的任何资源。 52 Close() error 53 54 //Heartbeat对硬件钱包执行健全检查,以查看是否 55 //仍然在线且健康。 56 Heartbeat() error 57 58 //派生向USB设备发送派生请求并返回以太坊 59 //地址位于该路径上。 60 Derive(path accounts.DerivationPath) (common.Address, error) 61 62 //signtx将事务发送到USB设备并等待用户确认 63 //或者拒绝交易。 64 SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) 65 } 66 67 //Wallet代表所有USB硬件共享的通用功能 68 //防止重新实施相同复杂维护机制的钱包 69 //对于不同的供应商。 70 type wallet struct { 71 hub *Hub //USB集线器扫描 72 driver driver //底层设备操作的硬件实现 73 url *accounts.URL //唯一标识此钱包的文本URL 74 75 info hid.DeviceInfo //已知的USB设备有关钱包的信息 76 device *hid.Device //USB设备作为硬件钱包做广告 77 78 accounts []accounts.Account //固定在硬件钱包上的派生帐户列表 79 paths map[common.Address]accounts.DerivationPath //签名操作的已知派生路径 80 81 deriveNextPath accounts.DerivationPath //帐户自动发现的下一个派生路径 82 deriveNextAddr common.Address //自动发现的下一个派生帐户地址 83 deriveChain ethereum.ChainStateReader //区块链状态阅读器发现用过的账户 84 deriveReq chan chan struct{} //请求自派生的通道 85 deriveQuit chan chan error //终止自导数的通道 86 87 healthQuit chan chan error 88 89 //锁定硬件钱包有点特别。因为硬件设备比较低 90 //执行时,与他们的任何通信可能需要 91 //时间。更糟糕的是,等待用户确认可能需要很长时间, 92 //但在这期间必须保持独家沟通。锁定整个钱包 93 //然而,在同一时间内,系统中任何不需要的部分都会停止运行。 94 //要进行通信,只需阅读一些状态(例如列出帐户)。 95 // 96 //因此,硬件钱包需要两个锁才能正常工作。国家 97 //锁可用于保护钱包软件侧的内部状态,其中 98 //不得在硬件通信期间独占。交流 99 //锁可以用来实现对设备本身的独占访问,这一个 100 //但是,应该允许“跳过”等待可能需要的操作 101 //使用该设备,但也可以不使用(例如,帐户自派生)。 102 // 103 //因为我们有两个锁,所以必须知道如何正确使用它们: 104 //-通信要求“device”不更改,因此获取 105 //commslock应该在有状态锁之后完成。 106 //-通信不得禁用对钱包状态的读取访问,因此 107 //只能将*read*锁保持为statelock。 108 commsLock chan struct{} //在不保持状态锁定的情况下,USB通信的互斥(buf=1) 109 stateLock sync.RWMutex //保护对wallet结构字段的读写访问 110 111 log log.Logger //上下文记录器,用其ID标记基 112 } 113 114 //url实现accounts.wallet,返回usb硬件设备的url。 115 func (w *wallet) URL() accounts.URL { 116 return *w.url //不可变,不需要锁 117 } 118 119 //状态实现accounts.wallet,从 120 //底层特定于供应商的硬件钱包实现。 121 func (w *wallet) Status() (string, error) { 122 w.stateLock.RLock() //没有设备通信,状态锁就足够了 123 defer w.stateLock.RUnlock() 124 125 status, failure := w.driver.Status() 126 if w.device == nil { 127 return "Closed", failure 128 } 129 return status, failure 130 } 131 132 //打开implements accounts.wallet,尝试打开与 133 //硬件钱包。 134 func (w *wallet) Open(passphrase string) error { 135 w.stateLock.Lock() //状态锁已经足够了,因为此时还没有连接 136 defer w.stateLock.Unlock() 137 138 //如果设备已打开一次,请拒绝重试 139 if w.paths != nil { 140 return accounts.ErrWalletAlreadyOpen 141 } 142 //确保实际设备连接仅完成一次 143 if w.device == nil { 144 device, err := w.info.Open() 145 if err != nil { 146 return err 147 } 148 w.device = device 149 w.commsLock = make(chan struct{}, 1) 150 w.commsLock <- struct{}{} //使能锁定 151 } 152 //将设备初始化委托给基础驱动程序 153 if err := w.driver.Open(w.device, passphrase); err != nil { 154 return err 155 } 156 //连接成功,开始生命周期管理 157 w.paths = make(map[common.Address]accounts.DerivationPath) 158 159 w.deriveReq = make(chan chan struct{}) 160 w.deriveQuit = make(chan chan error) 161 w.healthQuit = make(chan chan error) 162 163 go w.heartbeat() 164 go w.selfDerive() 165 166 //通知任何收听钱包事件的人可以访问新设备 167 go w.hub.updateFeed.Send(accounts.WalletEvent{Wallet: w, Kind: accounts.WalletOpened}) 168 169 return nil 170 } 171 172 //心跳是一个健康检查循环,用于USB钱包定期验证 173 //它们是否仍然存在,或者是否出现故障。 174 func (w *wallet) heartbeat() { 175 w.log.Debug("USB wallet health-check started") 176 defer w.log.Debug("USB wallet health-check stopped") 177 178 //执行心跳检查,直到终止或出错 179 var ( 180 errc chan error 181 err error 182 ) 183 for errc == nil && err == nil { 184 //等待直到请求终止或心跳周期到达 185 select { 186 case errc = <-w.healthQuit: 187 //请求终止 188 continue 189 case <-time.After(heartbeatCycle): 190 //心跳时间 191 } 192 //执行微小的数据交换以查看响应 193 w.stateLock.RLock() 194 if w.device == nil { 195 //在等待锁时终止 196 w.stateLock.RUnlock() 197 continue 198 } 199 <-w.commsLock //解析版本时不锁定状态 200 err = w.driver.Heartbeat() 201 w.commsLock <- struct{}{} 202 w.stateLock.RUnlock() 203 204 if err != nil { 205 w.stateLock.Lock() //锁定状态以将钱包撕下 206 w.close() 207 w.stateLock.Unlock() 208 } 209 //忽略与硬件无关的错误 210 err = nil 211 } 212 //如果出现错误,请等待终止 213 if err != nil { 214 w.log.Debug("USB wallet health-check failed", "err", err) 215 errc = <-w.healthQuit 216 } 217 errc <- err 218 } 219 220 //关闭机具帐户。钱包,关闭设备的USB连接。 221 func (w *wallet) Close() error { 222 //确保钱包已打开 223 w.stateLock.RLock() 224 hQuit, dQuit := w.healthQuit, w.deriveQuit 225 w.stateLock.RUnlock() 226 227 //终止健康检查 228 var herr error 229 if hQuit != nil { 230 errc := make(chan error) 231 hQuit <- errc 232 herr = <-errc //保存供以后使用,我们*必须*关闭USB 233 } 234 //终止自派生 235 var derr error 236 if dQuit != nil { 237 errc := make(chan error) 238 dQuit <- errc 239 derr = <-errc //保存供以后使用,我们*必须*关闭USB 240 } 241 //终止设备连接 242 w.stateLock.Lock() 243 defer w.stateLock.Unlock() 244 245 w.healthQuit = nil 246 w.deriveQuit = nil 247 w.deriveReq = nil 248 249 if err := w.close(); err != nil { 250 return err 251 } 252 if herr != nil { 253 return herr 254 } 255 return derr 256 } 257 258 //Close是内部钱包闭合器,用于终止USB连接和 259 //将所有字段重置为默认值。 260 // 261 //注意,CLOSE假设状态锁被保持! 262 func (w *wallet) close() error { 263 //允许重复关闭,特别是在运行状况检查失败时 264 if w.device == nil { 265 return nil 266 } 267 //关闭设备,清除所有内容,然后返回 268 w.device.Close() 269 w.device = nil 270 271 w.accounts, w.paths = nil, nil 272 w.driver.Close() 273 274 return nil 275 } 276 277 //帐户实现帐户。钱包,返回固定到的帐户列表 278 //USB硬件钱包。如果启用了自派生,则帐户列表为 279 //根据当前链状态定期扩展。 280 func (w *wallet) Accounts() []accounts.Account { 281 //如果正在运行,尝试自派生 282 reqc := make(chan struct{}, 1) 283 select { 284 case w.deriveReq <- reqc: 285 //已接受自派生请求,请稍候 286 <-reqc 287 default: 288 //脱机自派生、受限或忙碌、跳过 289 } 290 // 291 w.stateLock.RLock() 292 defer w.stateLock.RUnlock() 293 294 cpy := make([]accounts.Account, len(w.accounts)) 295 copy(cpy, w.accounts) 296 return cpy 297 } 298 299 //selfderive是一个帐户派生循环,在请求时尝试查找 300 //新的非零账户。 301 func (w *wallet) selfDerive() { 302 w.log.Debug("USB wallet self-derivation started") 303 defer w.log.Debug("USB wallet self-derivation stopped") 304 305 //执行自派生直到终止或出错 306 var ( 307 reqc chan struct{} 308 errc chan error 309 err error 310 ) 311 for errc == nil && err == nil { 312 //等待直到请求派生或终止 313 select { 314 case errc = <-w.deriveQuit: 315 //请求终止 316 continue 317 case reqc = <-w.deriveReq: 318 //已请求帐户发现 319 } 320 //派生需要链和设备访问,如果不可用则跳过 321 w.stateLock.RLock() 322 if w.device == nil || w.deriveChain == nil { 323 w.stateLock.RUnlock() 324 reqc <- struct{}{} 325 continue 326 } 327 select { 328 case <-w.commsLock: 329 default: 330 w.stateLock.RUnlock() 331 reqc <- struct{}{} 332 continue 333 } 334 //获取设备锁,派生下一批帐户 335 var ( 336 accs []accounts.Account 337 paths []accounts.DerivationPath 338 339 nextAddr = w.deriveNextAddr 340 nextPath = w.deriveNextPath 341 342 context = context.Background() 343 ) 344 for empty := false; !empty; { 345 //检索下一个派生的以太坊帐户 346 if nextAddr == (common.Address{}) { 347 if nextAddr, err = w.driver.Derive(nextPath); err != nil { 348 w.log.Warn("USB wallet account derivation failed", "err", err) 349 break 350 } 351 } 352 //对照当前链状态检查帐户状态 353 var ( 354 balance *big.Int 355 nonce uint64 356 ) 357 balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil) 358 if err != nil { 359 w.log.Warn("USB wallet balance retrieval failed", "err", err) 360 break 361 } 362 nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil) 363 if err != nil { 364 w.log.Warn("USB wallet nonce retrieval failed", "err", err) 365 break 366 } 367 //如果下一个帐户为空,请停止自派生,但仍要添加它。 368 if balance.Sign() == 0 && nonce == 0 { 369 empty = true 370 } 371 //我们刚刚自己创建了一个新帐户,开始在本地跟踪它 372 path := make(accounts.DerivationPath, len(nextPath)) 373 copy(path[:], nextPath[:]) 374 paths = append(paths, path) 375 376 account := accounts.Account{ 377 Address: nextAddr, 378 URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)}, 379 } 380 accs = append(accs, account) 381 382 //为新帐户(或以前为空的帐户)向用户显示日志消息 383 if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) { 384 w.log.Info("USB wallet discovered new account", "address", nextAddr, "path", path, "balance", balance, "nonce", nonce) 385 } 386 //获取下一个潜在帐户 387 if !empty { 388 nextAddr = common.Address{} 389 nextPath[len(nextPath)-1]++ 390 } 391 } 392 //自派生完成,释放装置锁 393 w.commsLock <- struct{}{} 394 w.stateLock.RUnlock() 395 396 //插入成功派生的任何帐户 397 w.stateLock.Lock() 398 for i := 0; i < len(accs); i++ { 399 if _, ok := w.paths[accs[i].Address]; !ok { 400 w.accounts = append(w.accounts, accs[i]) 401 w.paths[accs[i].Address] = paths[i] 402 } 403 } 404 //向前移动自派生 405 //TODO(卡拉贝拉):不要覆盖wallet.self派生的更改 406 w.deriveNextAddr = nextAddr 407 w.deriveNextPath = nextPath 408 w.stateLock.Unlock() 409 410 //一段时间后通知用户终止和循环(以避免损坏) 411 reqc <- struct{}{} 412 if err == nil { 413 select { 414 case errc = <-w.deriveQuit: 415 //请求终止,中止 416 case <-time.After(selfDeriveThrottling): 417 //等得够久,愿意自己再推导一次 418 } 419 } 420 } 421 //如果出现错误,请等待终止 422 if err != nil { 423 w.log.Debug("USB wallet self-derivation failed", "err", err) 424 errc = <-w.deriveQuit 425 } 426 errc <- err 427 } 428 429 //包含implements accounts.wallet,返回特定帐户是否为 430 //或未固定到此钱包实例中。尽管我们可以尝试解决 431 //取消固定帐户,这将是一个不可忽略的硬件操作。 432 func (w *wallet) Contains(account accounts.Account) bool { 433 w.stateLock.RLock() 434 defer w.stateLock.RUnlock() 435 436 _, exists := w.paths[account.Address] 437 return exists 438 } 439 440 //派生实现accounts.wallet,在特定的 441 //派生路径。如果pin设置为true,则帐户将添加到列表中 442 //个跟踪帐户。 443 func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Account, error) { 444 //如果成功,尝试派生实际帐户并更新其URL 445 w.stateLock.RLock() //避免设备在派生过程中消失 446 447 if w.device == nil { 448 w.stateLock.RUnlock() 449 return accounts.Account{}, accounts.ErrWalletClosed 450 } 451 <-w.commsLock //避免并行硬件访问 452 address, err := w.driver.Derive(path) 453 w.commsLock <- struct{}{} 454 455 w.stateLock.RUnlock() 456 457 //如果发生错误或未请求固定,请返回 458 if err != nil { 459 return accounts.Account{}, err 460 } 461 account := accounts.Account{ 462 Address: address, 463 URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)}, 464 } 465 if !pin { 466 return account, nil 467 } 468 // 469 w.stateLock.Lock() 470 defer w.stateLock.Unlock() 471 472 if _, ok := w.paths[address]; !ok { 473 w.accounts = append(w.accounts, account) 474 w.paths[address] = path 475 } 476 return account, nil 477 } 478 479 //selfderive实现accounts.wallet,尝试发现 480 //用户以前使用过(基于链状态),但他/她没有使用过 481 //手动明确地固定到钱包。为了避免链头监控,请自行 482 //派生仅在帐户列表期间运行(甚至在随后被限制)。 483 func (w *wallet) SelfDerive(base accounts.DerivationPath, chain ethereum.ChainStateReader) { 484 w.stateLock.Lock() 485 defer w.stateLock.Unlock() 486 487 w.deriveNextPath = make(accounts.DerivationPath, len(base)) 488 copy(w.deriveNextPath[:], base[:]) 489 490 w.deriveNextAddr = common.Address{} 491 w.deriveChain = chain 492 } 493 494 //signhash实现accounts.wallet,但是签名任意数据不是 495 //支持硬件钱包,因此此方法将始终返回错误。 496 func (w *wallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { 497 return nil, accounts.ErrNotSupported 498 } 499 500 //signtx实现accounts.wallet。它将交易发送到分类帐 501 //要求用户确认的钱包。它返回已签名的 502 //如果用户拒绝该事务,则为事务或失败。 503 // 504 //注意,如果运行在Ledger钱包上的以太坊应用程序的版本是 505 //太旧,无法签署EIP-155交易,但要求这样做,还是有错误 506 //将返回,而不是在宅基地模式中静默签名。 507 func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { 508 w.stateLock.RLock() //通信有自己的互斥,这是用于状态字段 509 defer w.stateLock.RUnlock() 510 511 //如果钱包关闭,中止 512 if w.device == nil { 513 return nil, accounts.ErrWalletClosed 514 } 515 //确保请求的帐户包含在 516 path, ok := w.paths[account.Address] 517 if !ok { 518 return nil, accounts.ErrUnknownAccount 519 } 520 //收集的所有信息和元数据均已签出,请求签名 521 <-w.commsLock 522 defer func() { w.commsLock <- struct{}{} }() 523 524 //在等待用户确认时,确保设备没有拧紧。 525 //TODO(karalabe):如果热插拔落在Windows上,则移除。 526 w.hub.commsLock.Lock() 527 w.hub.commsPend++ 528 w.hub.commsLock.Unlock() 529 530 defer func() { 531 w.hub.commsLock.Lock() 532 w.hub.commsPend-- 533 w.hub.commsLock.Unlock() 534 }() 535 //签署事务并验证发送方以避免硬件故障意外 536 sender, signed, err := w.driver.SignTx(path, tx, chainID) 537 if err != nil { 538 return nil, err 539 } 540 if sender != account.Address { 541 return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex()) 542 } 543 return signed, nil 544 } 545 546 //signhashwithpassphrase实现accounts.wallet,但是任意签名 547 //分类帐钱包不支持数据,因此此方法将始终返回 548 //一个错误。 549 func (w *wallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { 550 return w.SignHash(account, hash) 551 } 552 553 //signtxwithpassphrase实现accounts.wallet,尝试对给定的 554 //使用密码短语作为额外身份验证的给定帐户的事务。 555 //由于USB钱包不依赖密码,因此这些密码会被静默忽略。 556 func (w *wallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { 557 return w.SignTx(account, tx, chainID) 558 } 559