github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/signer/core/api.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:42</date> 10 //</624450110293544960> 11 12 13 package core 14 15 import ( 16 "context" 17 "encoding/json" 18 "errors" 19 "fmt" 20 "io/ioutil" 21 "math/big" 22 "reflect" 23 24 "github.com/ethereum/go-ethereum/accounts" 25 "github.com/ethereum/go-ethereum/accounts/keystore" 26 "github.com/ethereum/go-ethereum/accounts/usbwallet" 27 "github.com/ethereum/go-ethereum/common" 28 "github.com/ethereum/go-ethereum/common/hexutil" 29 "github.com/ethereum/go-ethereum/crypto" 30 "github.com/ethereum/go-ethereum/internal/ethapi" 31 "github.com/ethereum/go-ethereum/log" 32 "github.com/ethereum/go-ethereum/rlp" 33 ) 34 35 //number of accounts to derive用于硬件钱包,要派生的帐户数 36 const numberOfAccountsToDerive = 10 37 38 //ExternalAPI定义用于发出签名请求的外部API。 39 type ExternalAPI interface { 40 //列出可用帐户 41 List(ctx context.Context) ([]common.Address, error) 42 //创建新帐户的新请求 43 New(ctx context.Context) (accounts.Account, error) 44 //SignTransaction请求签署指定的事务 45 SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) 46 //签名-请求对给定数据进行签名(加前缀) 47 Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) 48 //导出-请求导出帐户 49 Export(ctx context.Context, addr common.Address) (json.RawMessage, error) 50 //导入-请求导入帐户 51 //在下一阶段,当我们 52 //双向通信 53 //导入(ctx context.context,keyjson json.rawmessage)(account,error) 54 } 55 56 //SignerRui指定UI需要实现什么方法才能用作签名者的UI 57 type SignerUI interface { 58 //approvetx提示用户确认请求签署交易 59 ApproveTx(request *SignTxRequest) (SignTxResponse, error) 60 //ApproveSignData提示用户确认请求签署数据 61 ApproveSignData(request *SignDataRequest) (SignDataResponse, error) 62 //approveexport提示用户确认导出加密帐户json 63 ApproveExport(request *ExportRequest) (ExportResponse, error) 64 //approveImport提示用户确认导入账号json 65 ApproveImport(request *ImportRequest) (ImportResponse, error) 66 //批准提示用户确认列出帐户 67 //用户界面可以修改要列出的科目列表 68 ApproveListing(request *ListRequest) (ListResponse, error) 69 //ApproveWaccount提示用户确认创建新帐户,并显示给调用方 70 ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) 71 //ShowError向用户显示错误消息 72 ShowError(message string) 73 //ShowInfo向用户显示信息消息 74 ShowInfo(message string) 75 //OnApprovedTX通知用户界面一个事务已成功签名。 76 //用户界面可以使用此方法跟踪发送给特定收件人的邮件数量。 77 OnApprovedTx(tx ethapi.SignTransactionResult) 78 //当签名者启动时调用OnSignerStartup,并告诉用户界面有关外部API位置和版本的信息。 79 //信息 80 OnSignerStartup(info StartupInfo) 81 //当CLEF需要用户输入时,调用OnInputRequired,例如主密码或 82 //解锁硬件钱包的PIN码 83 OnInputRequired(info UserInputRequest) (UserInputResponse, error) 84 } 85 86 //signerapi定义了externalAPI的实际实现 87 type SignerAPI struct { 88 chainID *big.Int 89 am *accounts.Manager 90 UI SignerUI 91 validator *Validator 92 rejectMode bool 93 } 94 95 //有关请求的元数据 96 type Metadata struct { 97 Remote string `json:"remote"` 98 Local string `json:"local"` 99 Scheme string `json:"scheme"` 100 UserAgent string `json:"User-Agent"` 101 Origin string `json:"Origin"` 102 } 103 104 //MetadataFromContext从给定的Context.Context中提取元数据 105 func MetadataFromContext(ctx context.Context) Metadata { 106 m := Metadata{"NA", "NA", "NA", "", ""} //蝙蝠侠 107 108 if v := ctx.Value("remote"); v != nil { 109 m.Remote = v.(string) 110 } 111 if v := ctx.Value("scheme"); v != nil { 112 m.Scheme = v.(string) 113 } 114 if v := ctx.Value("local"); v != nil { 115 m.Local = v.(string) 116 } 117 if v := ctx.Value("Origin"); v != nil { 118 m.Origin = v.(string) 119 } 120 if v := ctx.Value("User-Agent"); v != nil { 121 m.UserAgent = v.(string) 122 } 123 return m 124 } 125 126 //字符串实现字符串接口 127 func (m Metadata) String() string { 128 s, err := json.Marshal(m) 129 if err == nil { 130 return string(s) 131 } 132 return err.Error() 133 } 134 135 //签名者和用户界面之间的请求/响应类型的类型 136 type ( 137 //signtxrequest包含要签名的事务的信息 138 SignTxRequest struct { 139 Transaction SendTxArgs `json:"transaction"` 140 Callinfo []ValidationInfo `json:"call_info"` 141 Meta Metadata `json:"meta"` 142 } 143 //SigntxRequest的SigntxResponse结果 144 SignTxResponse struct { 145 //用户界面可以更改Tx 146 Transaction SendTxArgs `json:"transaction"` 147 Approved bool `json:"approved"` 148 Password string `json:"password"` 149 } 150 //将有关查询的信息导出到导出帐户 151 ExportRequest struct { 152 Address common.Address `json:"address"` 153 Meta Metadata `json:"meta"` 154 } 155 //导出响应对导出请求的响应 156 ExportResponse struct { 157 Approved bool `json:"approved"` 158 } 159 //导入请求有关导入帐户请求的信息 160 ImportRequest struct { 161 Meta Metadata `json:"meta"` 162 } 163 ImportResponse struct { 164 Approved bool `json:"approved"` 165 OldPassword string `json:"old_password"` 166 NewPassword string `json:"new_password"` 167 } 168 SignDataRequest struct { 169 Address common.MixedcaseAddress `json:"address"` 170 Rawdata hexutil.Bytes `json:"raw_data"` 171 Message string `json:"message"` 172 Hash hexutil.Bytes `json:"hash"` 173 Meta Metadata `json:"meta"` 174 } 175 SignDataResponse struct { 176 Approved bool `json:"approved"` 177 Password string 178 } 179 NewAccountRequest struct { 180 Meta Metadata `json:"meta"` 181 } 182 NewAccountResponse struct { 183 Approved bool `json:"approved"` 184 Password string `json:"password"` 185 } 186 ListRequest struct { 187 Accounts []Account `json:"accounts"` 188 Meta Metadata `json:"meta"` 189 } 190 ListResponse struct { 191 Accounts []Account `json:"accounts"` 192 } 193 Message struct { 194 Text string `json:"text"` 195 } 196 PasswordRequest struct { 197 Prompt string `json:"prompt"` 198 } 199 PasswordResponse struct { 200 Password string `json:"password"` 201 } 202 StartupInfo struct { 203 Info map[string]interface{} `json:"info"` 204 } 205 UserInputRequest struct { 206 Prompt string `json:"prompt"` 207 Title string `json:"title"` 208 IsPassword bool `json:"isPassword"` 209 } 210 UserInputResponse struct { 211 Text string `json:"text"` 212 } 213 ) 214 215 var ErrRequestDenied = errors.New("Request denied") 216 217 //NewSignerAPI创建了一个新的可用于帐户管理的API。 218 //kslocation指定存储受密码保护的private的目录 219 //创建新帐户时生成的键。 220 //nousb禁用支持硬件设备所需的USB支持,如 221 //Ledger和Trezor。 222 func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abidb *AbiDb, lightKDF bool, advancedMode bool) *SignerAPI { 223 var ( 224 backends []accounts.Backend 225 n, p = keystore.StandardScryptN, keystore.StandardScryptP 226 ) 227 if lightKDF { 228 n, p = keystore.LightScryptN, keystore.LightScryptP 229 } 230 //支持基于密码的帐户 231 if len(ksLocation) > 0 { 232 backends = append(backends, keystore.NewKeyStore(ksLocation, n, p)) 233 } 234 if advancedMode { 235 log.Info("Clef is in advanced mode: will warn instead of reject") 236 } 237 if !noUSB { 238 //启动用于分类帐硬件钱包的USB集线器 239 if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil { 240 log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err)) 241 } else { 242 backends = append(backends, ledgerhub) 243 log.Debug("Ledger support enabled") 244 } 245 //启动Trezor硬件钱包的USB集线器 246 if trezorhub, err := usbwallet.NewTrezorHub(); err != nil { 247 log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err)) 248 } else { 249 backends = append(backends, trezorhub) 250 log.Debug("Trezor support enabled") 251 } 252 } 253 signer := &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...), ui, NewValidator(abidb), !advancedMode} 254 if !noUSB { 255 signer.startUSBListener() 256 } 257 return signer 258 } 259 func (api *SignerAPI) openTrezor(url accounts.URL) { 260 resp, err := api.UI.OnInputRequired(UserInputRequest{ 261 Prompt: "Pin required to open Trezor wallet\n" + 262 "Look at the device for number positions\n\n" + 263 "7 | 8 | 9\n" + 264 "--+---+--\n" + 265 "4 | 5 | 6\n" + 266 "--+---+--\n" + 267 "1 | 2 | 3\n\n", 268 IsPassword: true, 269 Title: "Trezor unlock", 270 }) 271 if err != nil { 272 log.Warn("failed getting trezor pin", "err", err) 273 return 274 } 275 //我们使用的是URL而不是指向 276 //钱包——也许它已经不存在了 277 w, err := api.am.Wallet(url.String()) 278 if err != nil { 279 log.Warn("wallet unavailable", "url", url) 280 return 281 } 282 err = w.Open(resp.Text) 283 if err != nil { 284 log.Warn("failed to open wallet", "wallet", url, "err", err) 285 return 286 } 287 288 } 289 290 //StartusBlistener为USB事件启动监听器,用于硬件钱包交互 291 func (api *SignerAPI) startUSBListener() { 292 events := make(chan accounts.WalletEvent, 16) 293 am := api.am 294 am.Subscribe(events) 295 go func() { 296 297 //打开所有已连接的钱包 298 for _, wallet := range am.Wallets() { 299 if err := wallet.Open(""); err != nil { 300 log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err) 301 if err == usbwallet.ErrTrezorPINNeeded { 302 go api.openTrezor(wallet.URL()) 303 } 304 } 305 } 306 //听钱包事件直到终止 307 for event := range events { 308 switch event.Kind { 309 case accounts.WalletArrived: 310 if err := event.Wallet.Open(""); err != nil { 311 log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err) 312 if err == usbwallet.ErrTrezorPINNeeded { 313 go api.openTrezor(event.Wallet.URL()) 314 } 315 } 316 case accounts.WalletOpened: 317 status, _ := event.Wallet.Status() 318 log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status) 319 320 derivationPath := accounts.DefaultBaseDerivationPath 321 if event.Wallet.URL().Scheme == "ledger" { 322 derivationPath = accounts.DefaultLedgerBaseDerivationPath 323 } 324 var nextPath = derivationPath 325 //导出前n个帐户,目前已硬编码 326 for i := 0; i < numberOfAccountsToDerive; i++ { 327 acc, err := event.Wallet.Derive(nextPath, true) 328 if err != nil { 329 log.Warn("account derivation failed", "error", err) 330 } else { 331 log.Info("derived account", "address", acc.Address) 332 } 333 nextPath[len(nextPath)-1]++ 334 } 335 case accounts.WalletDropped: 336 log.Info("Old wallet dropped", "url", event.Wallet.URL()) 337 event.Wallet.Close() 338 } 339 } 340 }() 341 } 342 343 //list返回签名者管理的钱包集。每个钱包都可以包含 344 //多个帐户。 345 func (api *SignerAPI) List(ctx context.Context) ([]common.Address, error) { 346 var accs []Account 347 for _, wallet := range api.am.Wallets() { 348 for _, acc := range wallet.Accounts() { 349 acc := Account{Typ: "Account", URL: wallet.URL(), Address: acc.Address} 350 accs = append(accs, acc) 351 } 352 } 353 result, err := api.UI.ApproveListing(&ListRequest{Accounts: accs, Meta: MetadataFromContext(ctx)}) 354 if err != nil { 355 return nil, err 356 } 357 if result.Accounts == nil { 358 return nil, ErrRequestDenied 359 360 } 361 362 addresses := make([]common.Address, 0) 363 for _, acc := range result.Accounts { 364 addresses = append(addresses, acc.Address) 365 } 366 367 return addresses, nil 368 } 369 370 //新建创建新的密码保护帐户。私钥受保护 371 //给定的密码。用户负责备份存储的私钥 372 //在密钥库位置中,创建此API时指定了THA。 373 func (api *SignerAPI) New(ctx context.Context) (accounts.Account, error) { 374 be := api.am.Backends(keystore.KeyStoreType) 375 if len(be) == 0 { 376 return accounts.Account{}, errors.New("password based accounts not supported") 377 } 378 var ( 379 resp NewAccountResponse 380 err error 381 ) 382 //三次重试以获取有效密码 383 for i := 0; i < 3; i++ { 384 resp, err = api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}) 385 if err != nil { 386 return accounts.Account{}, err 387 } 388 if !resp.Approved { 389 return accounts.Account{}, ErrRequestDenied 390 } 391 if pwErr := ValidatePasswordFormat(resp.Password); pwErr != nil { 392 api.UI.ShowError(fmt.Sprintf("Account creation attempt #%d failed due to password requirements: %v", (i + 1), pwErr)) 393 } else { 394 //无误差 395 return be[0].(*keystore.KeyStore).NewAccount(resp.Password) 396 } 397 } 398 //否则将失败,并显示一般错误消息 399 return accounts.Account{}, errors.New("account creation failed") 400 } 401 402 //logdiff记录传入(原始)事务和从签名者返回的事务之间的差异。 403 //如果修改了事务,它还返回“true”,以便可以将签名者配置为不允许 404 //请求的用户界面修改 405 func logDiff(original *SignTxRequest, new *SignTxResponse) bool { 406 modified := false 407 if f0, f1 := original.Transaction.From, new.Transaction.From; !reflect.DeepEqual(f0, f1) { 408 log.Info("Sender-account changed by UI", "was", f0, "is", f1) 409 modified = true 410 } 411 if t0, t1 := original.Transaction.To, new.Transaction.To; !reflect.DeepEqual(t0, t1) { 412 log.Info("Recipient-account changed by UI", "was", t0, "is", t1) 413 modified = true 414 } 415 if g0, g1 := original.Transaction.Gas, new.Transaction.Gas; g0 != g1 { 416 modified = true 417 log.Info("Gas changed by UI", "was", g0, "is", g1) 418 } 419 if g0, g1 := big.Int(original.Transaction.GasPrice), big.Int(new.Transaction.GasPrice); g0.Cmp(&g1) != 0 { 420 modified = true 421 log.Info("GasPrice changed by UI", "was", g0, "is", g1) 422 } 423 if v0, v1 := big.Int(original.Transaction.Value), big.Int(new.Transaction.Value); v0.Cmp(&v1) != 0 { 424 modified = true 425 log.Info("Value changed by UI", "was", v0, "is", v1) 426 } 427 if d0, d1 := original.Transaction.Data, new.Transaction.Data; d0 != d1 { 428 d0s := "" 429 d1s := "" 430 if d0 != nil { 431 d0s = hexutil.Encode(*d0) 432 } 433 if d1 != nil { 434 d1s = hexutil.Encode(*d1) 435 } 436 if d1s != d0s { 437 modified = true 438 log.Info("Data changed by UI", "was", d0s, "is", d1s) 439 } 440 } 441 if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 { 442 modified = true 443 log.Info("Nonce changed by UI", "was", n0, "is", n1) 444 } 445 return modified 446 } 447 448 //signTransaction对给定的事务进行签名,并将其作为json和rlp编码的形式返回 449 func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) { 450 var ( 451 err error 452 result SignTxResponse 453 ) 454 msgs, err := api.validator.ValidateTransaction(&args, methodSelector) 455 if err != nil { 456 return nil, err 457 } 458 //如果我们处于“拒绝模式”,则拒绝而不是显示用户警告 459 if api.rejectMode { 460 if err := msgs.getWarnings(); err != nil { 461 return nil, err 462 } 463 } 464 465 req := SignTxRequest{ 466 Transaction: args, 467 Meta: MetadataFromContext(ctx), 468 Callinfo: msgs.Messages, 469 } 470 //工艺批准 471 result, err = api.UI.ApproveTx(&req) 472 if err != nil { 473 return nil, err 474 } 475 if !result.Approved { 476 return nil, ErrRequestDenied 477 } 478 //记录用户界面对签名请求所做的更改 479 logDiff(&req, &result) 480 var ( 481 acc accounts.Account 482 wallet accounts.Wallet 483 ) 484 acc = accounts.Account{Address: result.Transaction.From.Address()} 485 wallet, err = api.am.Find(acc) 486 if err != nil { 487 return nil, err 488 } 489 //将字段转换为实际事务 490 var unsignedTx = result.Transaction.toTransaction() 491 492 //要签名的是从UI返回的那个 493 signedTx, err := wallet.SignTxWithPassphrase(acc, result.Password, unsignedTx, api.chainID) 494 if err != nil { 495 api.UI.ShowError(err.Error()) 496 return nil, err 497 } 498 499 rlpdata, err := rlp.EncodeToBytes(signedTx) 500 response := ethapi.SignTransactionResult{Raw: rlpdata, Tx: signedTx} 501 502 //最后,将签名的Tx发送到UI 503 api.UI.OnApprovedTx(response) 504 //…和外部呼叫者 505 return &response, nil 506 507 } 508 509 //sign计算以太坊ECDSA签名: 510 //keccack256(“\x19ethereum签名消息:\n”+len(消息)+消息) 511 // 512 //注:生成的签名符合secp256k1曲线r、s和v值, 513 //由于遗产原因,V值将为27或28。 514 // 515 //用于计算签名的密钥用给定的密码解密。 516 // 517 //https://github.com/ethereum/go-ethereum/wiki/management-apis个人签名 518 func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) { 519 sighash, msg := SignHash(data) 520 //我们在查询是否有账户之前提出请求,以防止 521 //通过API进行帐户枚举 522 req := &SignDataRequest{Address: addr, Rawdata: data, Message: msg, Hash: sighash, Meta: MetadataFromContext(ctx)} 523 res, err := api.UI.ApproveSignData(req) 524 525 if err != nil { 526 return nil, err 527 } 528 if !res.Approved { 529 return nil, ErrRequestDenied 530 } 531 //查找包含请求签名者的钱包 532 account := accounts.Account{Address: addr.Address()} 533 wallet, err := api.am.Find(account) 534 if err != nil { 535 return nil, err 536 } 537 //集合用钱包签名数据 538 signature, err := wallet.SignHashWithPassphrase(account, res.Password, sighash) 539 if err != nil { 540 api.UI.ShowError(err.Error()) 541 return nil, err 542 } 543 signature[64] += 27 //根据黄纸将V从0/1转换为27/28 544 return signature, nil 545 } 546 547 //signhash是一个帮助函数,用于计算给定消息的哈希 548 //安全地用于计算签名。 549 // 550 //哈希计算为 551 //keccak256(“\x19ethereum签名消息:\n”$消息长度$消息)。 552 // 553 //这将为已签名的消息提供上下文,并防止对事务进行签名。 554 func SignHash(data []byte) ([]byte, string) { 555 msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) 556 return crypto.Keccak256([]byte(msg)), msg 557 } 558 559 //export以Web3密钥库格式返回与给定地址关联的加密私钥。 560 func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) { 561 res, err := api.UI.ApproveExport(&ExportRequest{Address: addr, Meta: MetadataFromContext(ctx)}) 562 563 if err != nil { 564 return nil, err 565 } 566 if !res.Approved { 567 return nil, ErrRequestDenied 568 } 569 //查找包含请求签名者的钱包 570 wallet, err := api.am.Find(accounts.Account{Address: addr}) 571 if err != nil { 572 return nil, err 573 } 574 if wallet.URL().Scheme != keystore.KeyStoreScheme { 575 return nil, fmt.Errorf("Account is not a keystore-account") 576 } 577 return ioutil.ReadFile(wallet.URL().Path) 578 } 579 580 //import尝试在本地密钥库中导入给定的keyjson。keyjson数据应为 581 //以Web3密钥库格式。它将使用给定的密码短语解密keyjson,并在成功时 582 //解密它将使用给定的新密码短语加密密钥,并将其存储在密钥库中。 583 //OBS!此方法已从公共API中删除。不应在外部API上公开 584 //有几个原因: 585 //1。即使它是加密的,它仍然应该被视为敏感数据。 586 //2。它可以用于dos clef,通过使用恶意数据,例如超大 587 //kdfarams的值。 588 func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage) (Account, error) { 589 be := api.am.Backends(keystore.KeyStoreType) 590 591 if len(be) == 0 { 592 return Account{}, errors.New("password based accounts not supported") 593 } 594 res, err := api.UI.ApproveImport(&ImportRequest{Meta: MetadataFromContext(ctx)}) 595 596 if err != nil { 597 return Account{}, err 598 } 599 if !res.Approved { 600 return Account{}, ErrRequestDenied 601 } 602 acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, res.OldPassword, res.NewPassword) 603 if err != nil { 604 api.UI.ShowError(err.Error()) 605 return Account{}, err 606 } 607 return Account{Typ: "Account", URL: acc.URL, Address: acc.Address}, nil 608 } 609