github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/accounts/usbwallet/trezor.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 19:16:32</date>
    10  //</624450066328850432>
    11  
    12  
    13  //此文件包含用于与Trezor硬件交互的实现
    14  //钱包。有线协议规范可在Satoshilabs网站上找到:
    15  //https://doc.satoshilabs.com/trezor-tech/api-protobuf.html网站
    16  
    17  package usbwallet
    18  
    19  import (
    20  	"encoding/binary"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"math/big"
    25  
    26  	"github.com/ethereum/go-ethereum/accounts"
    27  	"github.com/ethereum/go-ethereum/accounts/usbwallet/internal/trezor"
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/common/hexutil"
    30  	"github.com/ethereum/go-ethereum/core/types"
    31  	"github.com/ethereum/go-ethereum/log"
    32  	"github.com/golang/protobuf/proto"
    33  )
    34  
    35  //如果打开trezor需要PIN代码,则返回errtrezorpinneed。在
    36  //在这种情况下,调用应用程序应显示一个pinpad并将
    37  //编码的密码短语。
    38  var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
    39  
    40  //errTrezorreplyinvalidHeader是Trezor数据交换返回的错误消息
    41  //如果设备回复的标题不匹配。这通常意味着设备
    42  //处于浏览器模式。
    43  var errTrezorReplyInvalidHeader = errors.New("trezor: invalid reply header")
    44  
    45  //Trezordriver实现了与Trezor硬件钱包的通信。
    46  type trezorDriver struct {
    47  device  io.ReadWriter //通过USB设备连接进行通信
    48  version [3]uint32     //Trezor固件的当前版本
    49  label   string        //Trezor设备的当前文本标签
    50  pinwait bool          //标记设备是否正在等待PIN输入
    51  failure error         //任何使设备无法使用的故障
    52  log     log.Logger    //上下文记录器,用其ID标记Trezor
    53  }
    54  
    55  //NewTrezorDriver创建Trezor USB协议驱动程序的新实例。
    56  func newTrezorDriver(logger log.Logger) driver {
    57  	return &trezorDriver{
    58  		log: logger,
    59  	}
    60  }
    61  
    62  //状态实现帐户。钱包,无论Trezor是否打开、关闭
    63  //或者以太坊应用程序是否没有启动。
    64  func (w *trezorDriver) Status() (string, error) {
    65  	if w.failure != nil {
    66  		return fmt.Sprintf("Failed: %v", w.failure), w.failure
    67  	}
    68  	if w.device == nil {
    69  		return "Closed", w.failure
    70  	}
    71  	if w.pinwait {
    72  		return fmt.Sprintf("Trezor v%d.%d.%d '%s' waiting for PIN", w.version[0], w.version[1], w.version[2], w.label), w.failure
    73  	}
    74  	return fmt.Sprintf("Trezor v%d.%d.%d '%s' online", w.version[0], w.version[1], w.version[2], w.label), w.failure
    75  }
    76  
    77  //open实现usbwallet.driver,尝试初始化到的连接
    78  //Trezor硬件钱包。初始化trezor是一个两阶段操作:
    79  //*第一阶段是初始化连接并读取钱包的
    80  //特征。如果提供的密码短语为空,则调用此阶段。这个
    81  //设备将显示精确定位结果,并返回相应的
    82  //通知用户需要第二个打开阶段时出错。
    83  //*第二阶段是解锁对trezor的访问,由
    84  //用户实际上提供了一个将键盘键盘映射到PIN的密码短语
    85  //用户数(根据显示的精确定位随机排列)。
    86  func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
    87  	w.device, w.failure = device, nil
    88  
    89  //如果请求阶段1,初始化连接并等待用户回调
    90  	if passphrase == "" {
    91  //如果我们已经在等待密码输入,Insta返回
    92  		if w.pinwait {
    93  			return ErrTrezorPINNeeded
    94  		}
    95  //初始化与设备的连接
    96  		features := new(trezor.Features)
    97  		if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil {
    98  			return err
    99  		}
   100  		w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
   101  		w.label = features.GetLabel()
   102  
   103  //执行手动ping,强制设备请求其PIN
   104  		askPin := true
   105  		res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin}, new(trezor.PinMatrixRequest), new(trezor.Success))
   106  		if err != nil {
   107  			return err
   108  		}
   109  //只有在设备尚未解锁时才返回PIN请求
   110  		if res == 1 {
   111  return nil //设备以trezor.success响应。
   112  		}
   113  		w.pinwait = true
   114  		return ErrTrezorPINNeeded
   115  	}
   116  //第2阶段要求实际输入PIN
   117  	w.pinwait = false
   118  
   119  	if _, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success)); err != nil {
   120  		w.failure = err
   121  		return err
   122  	}
   123  	return nil
   124  }
   125  
   126  //close实现usbwallet.driver,清理和元数据维护在
   127  //Trezor司机。
   128  func (w *trezorDriver) Close() error {
   129  	w.version, w.label, w.pinwait = [3]uint32{}, "", false
   130  	return nil
   131  }
   132  
   133  //heartbeat实现usbwallet.driver,对
   134  //Trezor看看它是否仍然在线。
   135  func (w *trezorDriver) Heartbeat() error {
   136  	if _, err := w.trezorExchange(&trezor.Ping{}, new(trezor.Success)); err != nil {
   137  		w.failure = err
   138  		return err
   139  	}
   140  	return nil
   141  }
   142  
   143  //派生实现usbwallet.driver,向trezor发送派生请求
   144  //并返回位于该派生路径上的以太坊地址。
   145  func (w *trezorDriver) Derive(path accounts.DerivationPath) (common.Address, error) {
   146  	return w.trezorDerive(path)
   147  }
   148  
   149  //signtx实现usbwallet.driver,将事务发送到trezor并
   150  //正在等待用户确认或拒绝该事务。
   151  func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
   152  	if w.device == nil {
   153  		return common.Address{}, nil, accounts.ErrWalletClosed
   154  	}
   155  	return w.trezorSign(path, tx, chainID)
   156  }
   157  
   158  //TrezorDrive向Trezor设备发送派生请求并返回
   159  //以太坊地址位于该路径上。
   160  func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, error) {
   161  	address := new(trezor.EthereumAddress)
   162  	if _, err := w.trezorExchange(&trezor.EthereumGetAddress{AddressN: derivationPath}, address); err != nil {
   163  		return common.Address{}, err
   164  	}
   165  	return common.BytesToAddress(address.GetAddress()), nil
   166  }
   167  
   168  //Trezorsign将事务发送到Trezor钱包,并等待用户
   169  //确认或拒绝交易。
   170  func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
   171  //创建事务启动消息
   172  	data := tx.Data()
   173  	length := uint32(len(data))
   174  
   175  	request := &trezor.EthereumSignTx{
   176  		AddressN:   derivationPath,
   177  		Nonce:      new(big.Int).SetUint64(tx.Nonce()).Bytes(),
   178  		GasPrice:   tx.GasPrice().Bytes(),
   179  		GasLimit:   new(big.Int).SetUint64(tx.Gas()).Bytes(),
   180  		Value:      tx.Value().Bytes(),
   181  		DataLength: &length,
   182  	}
   183  	if to := tx.To(); to != nil {
   184  request.To = (*to)[:] //非合同部署,显式设置收件人
   185  	}
   186  if length > 1024 { //如果请求发送数据块
   187  		request.DataInitialChunk, data = data[:1024], data[1024:]
   188  	} else {
   189  		request.DataInitialChunk, data = data, nil
   190  	}
   191  if chainID != nil { //EIP-155事务,显式设置链ID(仅支持32位!?)
   192  		id := uint32(chainID.Int64())
   193  		request.ChainId = &id
   194  	}
   195  //发送初始消息和流内容,直到返回签名
   196  	response := new(trezor.EthereumTxRequest)
   197  	if _, err := w.trezorExchange(request, response); err != nil {
   198  		return common.Address{}, nil, err
   199  	}
   200  	for response.DataLength != nil && int(*response.DataLength) <= len(data) {
   201  		chunk := data[:*response.DataLength]
   202  		data = data[*response.DataLength:]
   203  
   204  		if _, err := w.trezorExchange(&trezor.EthereumTxAck{DataChunk: chunk}, response); err != nil {
   205  			return common.Address{}, nil, err
   206  		}
   207  	}
   208  //提取以太坊签名并进行健全性验证
   209  	if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 {
   210  		return common.Address{}, nil, errors.New("reply lacks signature")
   211  	}
   212  	signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV()))
   213  
   214  //基于链ID创建正确的签名者和签名转换
   215  	var signer types.Signer
   216  	if chainID == nil {
   217  		signer = new(types.HomesteadSigner)
   218  	} else {
   219  		signer = types.NewEIP155Signer(chainID)
   220  		signature[64] -= byte(chainID.Uint64()*2 + 35)
   221  	}
   222  //在事务中插入最终签名并检查发送者的健全性
   223  	signed, err := tx.WithSignature(signer, signature)
   224  	if err != nil {
   225  		return common.Address{}, nil, err
   226  	}
   227  	sender, err := types.Sender(signer, signed)
   228  	if err != nil {
   229  		return common.Address{}, nil, err
   230  	}
   231  	return sender, signed, nil
   232  }
   233  
   234  //trezor exchange执行与trezor钱包的数据交换,并向其发送
   235  //并检索响应。如果可能有多个响应,则
   236  //方法还将返回所用目标对象的索引。
   237  func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Message) (int, error) {
   238  //构造原始消息有效负载以进行分组
   239  	data, err := proto.Marshal(req)
   240  	if err != nil {
   241  		return 0, err
   242  	}
   243  	payload := make([]byte, 8+len(data))
   244  	copy(payload, []byte{0x23, 0x23})
   245  	binary.BigEndian.PutUint16(payload[2:], trezor.Type(req))
   246  	binary.BigEndian.PutUint32(payload[4:], uint32(len(data)))
   247  	copy(payload[8:], data)
   248  
   249  //将所有块流式传输到设备
   250  	chunk := make([]byte, 64)
   251  chunk[0] = 0x3f //报告ID幻数
   252  
   253  	for len(payload) > 0 {
   254  //构造新消息到流,如果需要,用零填充
   255  		if len(payload) > 63 {
   256  			copy(chunk[1:], payload[:63])
   257  			payload = payload[63:]
   258  		} else {
   259  			copy(chunk[1:], payload)
   260  			copy(chunk[1+len(payload):], make([]byte, 63-len(payload)))
   261  			payload = nil
   262  		}
   263  //发送到设备
   264  		w.log.Trace("Data chunk sent to the Trezor", "chunk", hexutil.Bytes(chunk))
   265  		if _, err := w.device.Write(chunk); err != nil {
   266  			return 0, err
   267  		}
   268  	}
   269  //将回复从钱包中以64字节的块流式返回
   270  	var (
   271  		kind  uint16
   272  		reply []byte
   273  	)
   274  	for {
   275  //从Trezor钱包中读取下一块
   276  		if _, err := io.ReadFull(w.device, chunk); err != nil {
   277  			return 0, err
   278  		}
   279  		w.log.Trace("Data chunk received from the Trezor", "chunk", hexutil.Bytes(chunk))
   280  
   281  //确保传输头匹配
   282  		if chunk[0] != 0x3f || (len(reply) == 0 && (chunk[1] != 0x23 || chunk[2] != 0x23)) {
   283  			return 0, errTrezorReplyInvalidHeader
   284  		}
   285  //如果是第一个块,则检索回复消息类型和总消息长度
   286  		var payload []byte
   287  
   288  		if len(reply) == 0 {
   289  			kind = binary.BigEndian.Uint16(chunk[3:5])
   290  			reply = make([]byte, 0, int(binary.BigEndian.Uint32(chunk[5:9])))
   291  			payload = chunk[9:]
   292  		} else {
   293  			payload = chunk[1:]
   294  		}
   295  //追加到答复并在填写时停止
   296  		if left := cap(reply) - len(reply); left > len(payload) {
   297  			reply = append(reply, payload...)
   298  		} else {
   299  			reply = append(reply, payload[:left]...)
   300  			break
   301  		}
   302  	}
   303  //尝试将答复解析为请求的答复消息
   304  	if kind == uint16(trezor.MessageType_MessageType_Failure) {
   305  //Trezor返回一个失败,提取并返回消息
   306  		failure := new(trezor.Failure)
   307  		if err := proto.Unmarshal(reply, failure); err != nil {
   308  			return 0, err
   309  		}
   310  		return 0, errors.New("trezor: " + failure.GetMessage())
   311  	}
   312  	if kind == uint16(trezor.MessageType_MessageType_ButtonRequest) {
   313  //Trezor正在等待用户确认、确认并等待下一条消息
   314  		return w.trezorExchange(&trezor.ButtonAck{}, results...)
   315  	}
   316  	for i, res := range results {
   317  		if trezor.Type(res) == kind {
   318  			return i, proto.Unmarshal(reply, res)
   319  		}
   320  	}
   321  	expected := make([]string, len(results))
   322  	for i, res := range results {
   323  		expected[i] = trezor.Name(trezor.Type(res))
   324  	}
   325  	return 0, fmt.Errorf("trezor: expected reply types %s, got %s", expected, trezor.Name(kind))
   326  }
   327