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