github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/accounts/usbwallet/ledger.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  //此文件是Go以太坊库的一部分。
    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  //此文件包含用于与分类帐硬件交互的实现
    26  //钱包。有线协议规范可在分类账Blue Github报告中找到:
    27  //https://raw.githubusercontent.com/ledgerhq/blue-app-eth/master/doc/eth app.asc
    28  
    29  package usbwallet
    30  
    31  import (
    32  	"encoding/binary"
    33  	"encoding/hex"
    34  	"errors"
    35  	"fmt"
    36  	"io"
    37  	"math/big"
    38  
    39  	"github.com/ethereum/go-ethereum/accounts"
    40  	"github.com/ethereum/go-ethereum/common"
    41  	"github.com/ethereum/go-ethereum/common/hexutil"
    42  	"github.com/ethereum/go-ethereum/core/types"
    43  	"github.com/ethereum/go-ethereum/log"
    44  	"github.com/ethereum/go-ethereum/rlp"
    45  )
    46  
    47  //LedgerProcode是对支持的分类帐操作码进行编码的枚举。
    48  type ledgerOpcode byte
    49  
    50  //LedgerParam1是一个枚举,用于编码支持的分类帐参数
    51  //特定操作码。相同的参数值可以在操作码之间重复使用。
    52  type ledgerParam1 byte
    53  
    54  //LedgerParam2是一个枚举,用于编码支持的分类帐参数
    55  //特定操作码。相同的参数值可以在操作码之间重复使用。
    56  type ledgerParam2 byte
    57  
    58  const (
    59  ledgerOpRetrieveAddress  ledgerOpcode = 0x02 //返回给定BIP 32路径的公钥和以太坊地址
    60  ledgerOpSignTransaction  ledgerOpcode = 0x04 //让用户验证参数后签署以太坊事务
    61  ledgerOpGetConfiguration ledgerOpcode = 0x06 //返回特定钱包应用程序配置
    62  
    63  ledgerP1DirectlyFetchAddress    ledgerParam1 = 0x00 //直接从钱包返回地址
    64  ledgerP1InitTransactionData     ledgerParam1 = 0x00 //用于签名的第一个事务数据块
    65  ledgerP1ContTransactionData     ledgerParam1 = 0x80 //用于签名的后续事务数据块
    66  ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 //不要随地址返回链代码
    67  )
    68  
    69  //errledgereplyinvalidheader是分类帐数据交换返回的错误消息。
    70  //如果设备回复的标题不匹配。这通常意味着设备
    71  //处于浏览器模式。
    72  var errLedgerReplyInvalidHeader = errors.New("ledger: invalid reply header")
    73  
    74  //errlegrinvalidversionreply是由分类帐版本检索返回的错误消息
    75  //当响应确实到达,但它不包含预期的数据时。
    76  var errLedgerInvalidVersionReply = errors.New("ledger: invalid version reply")
    77  
    78  //LedgerDriver实现与Ledger硬件钱包的通信。
    79  type ledgerDriver struct {
    80  device  io.ReadWriter //通过USB设备连接进行通信
    81  version [3]byte       //分类帐固件的当前版本(如果应用程序脱机,则为零)
    82  browser bool          //标记分类帐是否处于浏览器模式(答复通道不匹配)
    83  failure error         //任何使设备无法使用的故障
    84  log     log.Logger    //上下文记录器,用其ID标记分类帐
    85  }
    86  
    87  //NewledgerDriver创建LedgerUSB协议驱动程序的新实例。
    88  func newLedgerDriver(logger log.Logger) driver {
    89  	return &ledgerDriver{
    90  		log: logger,
    91  	}
    92  }
    93  
    94  //状态实现usbwallet.driver,返回分类帐可以返回的各种状态
    95  //目前在。
    96  func (w *ledgerDriver) Status() (string, error) {
    97  	if w.failure != nil {
    98  		return fmt.Sprintf("Failed: %v", w.failure), w.failure
    99  	}
   100  	if w.browser {
   101  		return "Ethereum app in browser mode", w.failure
   102  	}
   103  	if w.offline() {
   104  		return "Ethereum app offline", w.failure
   105  	}
   106  	return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2]), w.failure
   107  }
   108  
   109  //离线返回钱包和以太坊应用程序是否离线。
   110  //
   111  //该方法假定状态锁被保持!
   112  func (w *ledgerDriver) offline() bool {
   113  	return w.version == [3]byte{0, 0, 0}
   114  }
   115  
   116  //open实现usbwallet.driver,尝试初始化到的连接
   117  //分类帐硬件钱包。分类帐不需要用户密码,因此
   118  //参数被静默丢弃。
   119  func (w *ledgerDriver) Open(device io.ReadWriter, passphrase string) error {
   120  	w.device, w.failure = device, nil
   121  
   122  	_, err := w.ledgerDerive(accounts.DefaultBaseDerivationPath)
   123  	if err != nil {
   124  //以太坊应用程序未运行或处于浏览器模式,无需执行其他操作,返回
   125  		if err == errLedgerReplyInvalidHeader {
   126  			w.browser = true
   127  		}
   128  		return nil
   129  	}
   130  //尝试解析以太坊应用程序的版本,将在v1.0.2之前失败
   131  	if w.version, err = w.ledgerVersion(); err != nil {
   132  w.version = [3]byte{1, 0, 0} //假设最坏情况,无法验证v1.0.0或v1.0.1
   133  	}
   134  	return nil
   135  }
   136  
   137  //close实现usbwallet.driver,清理和元数据维护在
   138  //分类帐驱动程序。
   139  func (w *ledgerDriver) Close() error {
   140  	w.browser, w.version = false, [3]byte{}
   141  	return nil
   142  }
   143  
   144  //heartbeat实现usbwallet.driver,对
   145  //看它是否仍然在线。
   146  func (w *ledgerDriver) Heartbeat() error {
   147  	if _, err := w.ledgerVersion(); err != nil && err != errLedgerInvalidVersionReply {
   148  		w.failure = err
   149  		return err
   150  	}
   151  	return nil
   152  }
   153  
   154  //派生实现usbwallet.driver,向分类帐发送派生请求
   155  //并返回位于该派生路径上的以太坊地址。
   156  func (w *ledgerDriver) Derive(path accounts.DerivationPath) (common.Address, error) {
   157  	return w.ledgerDerive(path)
   158  }
   159  
   160  //signtx实现usbwallet.driver,将事务发送到分类帐并
   161  //正在等待用户确认或拒绝该事务。
   162  //
   163  //注意,如果运行在Ledger钱包上的以太坊应用程序的版本是
   164  //太旧,无法签署EIP-155交易,但要求这样做,还是有错误
   165  //将返回,而不是在宅基地模式中静默签名。
   166  func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
   167  //如果以太坊应用程序不运行,则中止
   168  	if w.offline() {
   169  		return common.Address{}, nil, accounts.ErrWalletClosed
   170  	}
   171  //确保钱包能够签署给定的交易
   172  	if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
   173  		return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
   174  	}
   175  //收集的所有信息和元数据均已签出,请求签名
   176  	return w.ledgerSign(path, tx, chainID)
   177  }
   178  
   179  //LedgerVersion检索正在运行的以太坊钱包应用程序的当前版本
   180  //在分类帐钱包上。
   181  //
   182  //版本检索协议定义如下:
   183  //
   184  //CLA INS P1 P2 LC LE
   185  //---+-----+---+---+---+----
   186  //e0 06 00 00 04
   187  //
   188  //没有输入数据,输出数据为:
   189  //
   190  //说明长度
   191  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
   192  //标志01:用户1字节启用的任意数据签名
   193  //应用程序主版本1字节
   194  //应用程序次要版本1字节
   195  //应用补丁版本1字节
   196  func (w *ledgerDriver) ledgerVersion() ([3]byte, error) {
   197  //发送请求并等待响应
   198  	reply, err := w.ledgerExchange(ledgerOpGetConfiguration, 0, 0, nil)
   199  	if err != nil {
   200  		return [3]byte{}, err
   201  	}
   202  	if len(reply) != 4 {
   203  		return [3]byte{}, errLedgerInvalidVersionReply
   204  	}
   205  //缓存版本以备将来参考
   206  	var version [3]byte
   207  	copy(version[:], reply[1:])
   208  	return version, nil
   209  }
   210  
   211  //LedgerDrive从分类帐中检索当前活动的以太坊地址
   212  //钱包在指定的衍生路径。
   213  //
   214  //地址派生协议定义如下:
   215  //
   216  //CLA INS P1 P2 LC LE
   217  //---+-----+---+---+---+----
   218  //e0 02 00返回地址
   219  //01显示地址,返回前确认
   220  //00:不返回链码
   221  //01:返回链码
   222  //γvar 00
   223  //
   224  //其中输入数据为:
   225  //
   226  //说明长度
   227  //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
   228  //要执行的BIP 32派生数(最大10)1字节
   229  //第一个派生索引(big endian)4字节
   230  //……4字节
   231  //上次派生索引(big endian)4字节
   232  //
   233  //输出数据为:
   234  //
   235  //说明长度
   236  //——————————+———————————————————
   237  //公钥长度1字节
   238  //未压缩公钥任意
   239  //以太坊地址长度1字节
   240  //以太坊地址40字节十六进制ASCII
   241  //链码(如果要求)32字节
   242  func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, error) {
   243  //将派生路径展平到分类帐请求中
   244  	path := make([]byte, 1+4*len(derivationPath))
   245  	path[0] = byte(len(derivationPath))
   246  	for i, component := range derivationPath {
   247  		binary.BigEndian.PutUint32(path[1+4*i:], component)
   248  	}
   249  //发送请求并等待响应
   250  	reply, err := w.ledgerExchange(ledgerOpRetrieveAddress, ledgerP1DirectlyFetchAddress, ledgerP2DiscardAddressChainCode, path)
   251  	if err != nil {
   252  		return common.Address{}, err
   253  	}
   254  //丢弃公钥,我们暂时不需要它
   255  	if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
   256  		return common.Address{}, errors.New("reply lacks public key entry")
   257  	}
   258  	reply = reply[1+int(reply[0]):]
   259  
   260  //提取以太坊十六进制地址字符串
   261  	if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
   262  		return common.Address{}, errors.New("reply lacks address entry")
   263  	}
   264  	hexstr := reply[1 : 1+int(reply[0])]
   265  
   266  //将十六进制sting解码为以太坊地址并返回
   267  	var address common.Address
   268  	hex.Decode(address[:], hexstr)
   269  	return address, nil
   270  }
   271  
   272  //Ledgersign将交易发送到Ledger钱包,并等待用户
   273  //确认或拒绝交易。
   274  //
   275  //事务签名协议定义如下:
   276  //
   277  //CLA INS P1 P2 LC LE
   278  //---+-----+---+---+---+----
   279  //e0 04 00:第一个事务数据块
   280  //80:后续交易数据块
   281  //00变量变量
   282  //
   283  //其中,第一个事务块(前255个字节)的输入为:
   284  //
   285  //说明长度
   286  //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
   287  //要执行的BIP 32派生数(最大10)1字节
   288  //第一个派生索引(big endian)4字节
   289  //……4字节
   290  //上次派生索引(big endian)4字节
   291  //
   292  //
   293  //后续事务块(前255个字节)的输入为:
   294  //
   295  //说明长度
   296  //————————+——————————
   297  //RLP事务块任意
   298  //
   299  //输出数据为:
   300  //
   301  //说明长度
   302  //-----------+-----
   303  //签名v 1字节
   304  //签名R 32字节
   305  //签名S 32字节
   306  func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction, chainID *big.Int) (common.Address, *types.Transaction, error) {
   307  //将派生路径展平到分类帐请求中
   308  	path := make([]byte, 1+4*len(derivationPath))
   309  	path[0] = byte(len(derivationPath))
   310  	for i, component := range derivationPath {
   311  		binary.BigEndian.PutUint32(path[1+4*i:], component)
   312  	}
   313  //根据请求的是遗留签名还是EIP155签名创建事务RLP
   314  	var (
   315  		txrlp []byte
   316  		err   error
   317  	)
   318  	if chainID == nil {
   319  		if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil {
   320  			return common.Address{}, nil, err
   321  		}
   322  	} else {
   323  		if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), chainID, big.NewInt(0), big.NewInt(0)}); err != nil {
   324  			return common.Address{}, nil, err
   325  		}
   326  	}
   327  	payload := append(path, txrlp...)
   328  
   329  //发送请求并等待响应
   330  	var (
   331  		op    = ledgerP1InitTransactionData
   332  		reply []byte
   333  	)
   334  	for len(payload) > 0 {
   335  //计算下一个数据块的大小
   336  		chunk := 255
   337  		if chunk > len(payload) {
   338  			chunk = len(payload)
   339  		}
   340  //发送块,确保正确处理
   341  		reply, err = w.ledgerExchange(ledgerOpSignTransaction, op, 0, payload[:chunk])
   342  		if err != nil {
   343  			return common.Address{}, nil, err
   344  		}
   345  //移动有效负载并确保后续块标记为
   346  		payload = payload[chunk:]
   347  		op = ledgerP1ContTransactionData
   348  	}
   349  //提取以太坊签名并进行健全性验证
   350  	if len(reply) != 65 {
   351  		return common.Address{}, nil, errors.New("reply lacks signature")
   352  	}
   353  	signature := append(reply[1:], reply[0])
   354  
   355  //基于链ID创建正确的签名者和签名转换
   356  	var signer types.Signer
   357  	if chainID == nil {
   358  		signer = new(types.HomesteadSigner)
   359  	} else {
   360  		signer = types.NewEIP155Signer(chainID)
   361  		signature[64] = signature[64] - byte(chainID.Uint64()*2+35)
   362  	}
   363  	signed, err := tx.WithSignature(signer, signature)
   364  	if err != nil {
   365  		return common.Address{}, nil, err
   366  	}
   367  	sender, err := types.Sender(signer, signed)
   368  	if err != nil {
   369  		return common.Address{}, nil, err
   370  	}
   371  	return sender, signed, nil
   372  }
   373  
   374  //LedgerxChange执行与Ledger钱包的数据交换,并向其发送
   375  //并检索响应。
   376  //
   377  //公共传输头定义如下:
   378  //
   379  //说明长度
   380  //————————————————————————————————————————————————————————————————————————————————————————————————————————————————
   381  //通信信道ID(big endian)2字节
   382  //命令标记1字节
   383  //包序列索引(big endian)2字节
   384  //有效载荷任意
   385  //
   386  //通信信道ID允许命令在同一个信道上复用
   387  //物理链路。暂时不使用,应设置为0101
   388  //为了避免与忽略前导00字节的实现的兼容性问题。
   389  //
   390  //命令标记描述消息内容。使用标签“apdu(0x05)”作为标准
   391  //apdu有效负载,或标记\ping(0x02),用于简单链接测试。
   392  //
   393  //包序列索引描述了分段有效负载的当前序列。
   394  //第一个片段索引是0x00。
   395  //
   396  //APDU命令有效负载编码如下:
   397  //
   398  //说明长度
   399  //—————————————————————————
   400  //apdu长度(big endian)2字节
   401  //apdu cla 1字节
   402  //apdu-ins 1字节
   403  //APDU P1 1字节
   404  //apdu p2 1字节
   405  //apdu长度1字节
   406  //可选APDU数据任意
   407  func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 ledgerParam2, data []byte) ([]byte, error) {
   408  //构造消息有效负载,可能拆分为多个块
   409  	apdu := make([]byte, 2, 7+len(data))
   410  
   411  	binary.BigEndian.PutUint16(apdu, uint16(5+len(data)))
   412  	apdu = append(apdu, []byte{0xe0, byte(opcode), byte(p1), byte(p2), byte(len(data))}...)
   413  	apdu = append(apdu, data...)
   414  
   415  //将所有块流式传输到设备
   416  header := []byte{0x01, 0x01, 0x05, 0x00, 0x00} //附加了通道ID和命令标记
   417  	chunk := make([]byte, 64)
   418  	space := len(chunk) - len(header)
   419  
   420  	for i := 0; len(apdu) > 0; i++ {
   421  //构造要传输的新消息
   422  		chunk = append(chunk[:0], header...)
   423  		binary.BigEndian.PutUint16(chunk[3:], uint16(i))
   424  
   425  		if len(apdu) > space {
   426  			chunk = append(chunk, apdu[:space]...)
   427  			apdu = apdu[space:]
   428  		} else {
   429  			chunk = append(chunk, apdu...)
   430  			apdu = nil
   431  		}
   432  //发送到设备
   433  		w.log.Trace("Data chunk sent to the Ledger", "chunk", hexutil.Bytes(chunk))
   434  		if _, err := w.device.Write(chunk); err != nil {
   435  			return nil, err
   436  		}
   437  	}
   438  //将回复从钱包中以64字节的块流式返回
   439  	var reply []byte
   440  chunk = chunk[:64] //是的,我们肯定有足够的空间
   441  	for {
   442  //从分类帐钱包中读取下一块
   443  		if _, err := io.ReadFull(w.device, chunk); err != nil {
   444  			return nil, err
   445  		}
   446  		w.log.Trace("Data chunk received from the Ledger", "chunk", hexutil.Bytes(chunk))
   447  
   448  //确保传输头匹配
   449  		if chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != 0x05 {
   450  			return nil, errLedgerReplyInvalidHeader
   451  		}
   452  //如果是第一个块,则检索消息的总长度
   453  		var payload []byte
   454  
   455  		if chunk[3] == 0x00 && chunk[4] == 0x00 {
   456  			reply = make([]byte, 0, int(binary.BigEndian.Uint16(chunk[5:7])))
   457  			payload = chunk[7:]
   458  		} else {
   459  			payload = chunk[5:]
   460  		}
   461  //追加到答复并在填写时停止
   462  		if left := cap(reply) - len(reply); left > len(payload) {
   463  			reply = append(reply, payload...)
   464  		} else {
   465  			reply = append(reply, payload[:left]...)
   466  			break
   467  		}
   468  	}
   469  	return reply[:len(reply)-2], nil
   470  }