github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/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 12:09:27</date> 10 //</624342587083329536> 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] = 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