github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/signer/core/api.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 //版权所有2018 Go Ethereum作者 10 //此文件是Go以太坊的一部分。 11 // 12 //Go以太坊是免费软件:您可以重新发布和/或修改它 13 //根据GNU通用公共许可证的条款 14 //自由软件基金会,或者许可证的第3版,或者 15 //(由您选择)任何更高版本。 16 // 17 //Go以太坊的分布希望它会有用, 18 //但没有任何保证;甚至没有 19 //适销性或特定用途的适用性。见 20 //GNU通用公共许可证了解更多详细信息。 21 // 22 //你应该已经收到一份GNU通用公共许可证的副本 23 //一起去以太坊吧。如果没有,请参见<http://www.gnu.org/licenses/>。 24 25 package core 26 27 import ( 28 "context" 29 "encoding/json" 30 "errors" 31 "fmt" 32 "io/ioutil" 33 "math/big" 34 "reflect" 35 36 "github.com/ethereum/go-ethereum/accounts" 37 "github.com/ethereum/go-ethereum/accounts/keystore" 38 "github.com/ethereum/go-ethereum/accounts/usbwallet" 39 "github.com/ethereum/go-ethereum/common" 40 "github.com/ethereum/go-ethereum/common/hexutil" 41 "github.com/ethereum/go-ethereum/crypto" 42 "github.com/ethereum/go-ethereum/internal/ethapi" 43 "github.com/ethereum/go-ethereum/log" 44 "github.com/ethereum/go-ethereum/rlp" 45 ) 46 47 //ExternalAPI定义用于发出签名请求的外部API。 48 type ExternalAPI interface { 49 //列出可用帐户 50 List(ctx context.Context) (Accounts, error) 51 //创建新帐户的新请求 52 New(ctx context.Context) (accounts.Account, error) 53 //SignTransaction请求签署指定的事务 54 SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) 55 //签名-请求对给定数据进行签名(加前缀) 56 Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) 57 //ecrecover-请求执行ecrecover 58 EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) 59 //导出-请求导出帐户 60 Export(ctx context.Context, addr common.Address) (json.RawMessage, error) 61 //导入-请求导入帐户 62 Import(ctx context.Context, keyJSON json.RawMessage) (Account, error) 63 } 64 65 //SignerRui指定UI需要实现什么方法才能用作签名者的UI 66 type SignerUI interface { 67 //approvetx提示用户确认请求签署交易 68 ApproveTx(request *SignTxRequest) (SignTxResponse, error) 69 //ApproveSignData提示用户确认请求签署数据 70 ApproveSignData(request *SignDataRequest) (SignDataResponse, error) 71 //approveexport提示用户确认导出加密帐户json 72 ApproveExport(request *ExportRequest) (ExportResponse, error) 73 //approveImport提示用户确认导入账号json 74 ApproveImport(request *ImportRequest) (ImportResponse, error) 75 //批准提示用户确认列出帐户 76 //用户界面可以修改要列出的科目列表 77 ApproveListing(request *ListRequest) (ListResponse, error) 78 //ApproveWaccount提示用户确认创建新帐户,并显示给调用方 79 ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) 80 //ShowError向用户显示错误消息 81 ShowError(message string) 82 //ShowInfo向用户显示信息消息 83 ShowInfo(message string) 84 //OnApprovedTX通知用户界面一个事务已成功签名。 85 //用户界面可以使用此方法跟踪发送给特定收件人的邮件数量。 86 OnApprovedTx(tx ethapi.SignTransactionResult) 87 //当签名者启动时调用OnSignerStartup,并告诉用户界面有关外部API位置和版本的信息。 88 //信息 89 OnSignerStartup(info StartupInfo) 90 } 91 92 //signerapi定义了externalAPI的实际实现 93 type SignerAPI struct { 94 chainID *big.Int 95 am *accounts.Manager 96 UI SignerUI 97 validator *Validator 98 } 99 100 //有关请求的元数据 101 type Metadata struct { 102 Remote string `json:"remote"` 103 Local string `json:"local"` 104 Scheme string `json:"scheme"` 105 } 106 107 //MetadataFromContext从给定的Context.Context中提取元数据 108 func MetadataFromContext(ctx context.Context) Metadata { 109 m := Metadata{"NA", "NA", "NA"} //蝙蝠侠 110 111 if v := ctx.Value("remote"); v != nil { 112 m.Remote = v.(string) 113 } 114 if v := ctx.Value("scheme"); v != nil { 115 m.Scheme = v.(string) 116 } 117 if v := ctx.Value("local"); v != nil { 118 m.Local = v.(string) 119 } 120 return m 121 } 122 123 //字符串实现字符串接口 124 func (m Metadata) String() string { 125 s, err := json.Marshal(m) 126 if err == nil { 127 return string(s) 128 } 129 return err.Error() 130 } 131 132 //签名者和用户界面之间的请求/响应类型的类型 133 type ( 134 //signtxrequest包含要签名的事务的信息 135 SignTxRequest struct { 136 Transaction SendTxArgs `json:"transaction"` 137 Callinfo []ValidationInfo `json:"call_info"` 138 Meta Metadata `json:"meta"` 139 } 140 //SigntxRequest的SigntxResponse结果 141 SignTxResponse struct { 142 //用户界面可以更改Tx 143 Transaction SendTxArgs `json:"transaction"` 144 Approved bool `json:"approved"` 145 Password string `json:"password"` 146 } 147 //将有关查询的信息导出到导出帐户 148 ExportRequest struct { 149 Address common.Address `json:"address"` 150 Meta Metadata `json:"meta"` 151 } 152 //导出响应对导出请求的响应 153 ExportResponse struct { 154 Approved bool `json:"approved"` 155 } 156 //导入请求有关导入帐户请求的信息 157 ImportRequest struct { 158 Meta Metadata `json:"meta"` 159 } 160 ImportResponse struct { 161 Approved bool `json:"approved"` 162 OldPassword string `json:"old_password"` 163 NewPassword string `json:"new_password"` 164 } 165 SignDataRequest struct { 166 Address common.MixedcaseAddress `json:"address"` 167 Rawdata hexutil.Bytes `json:"raw_data"` 168 Message string `json:"message"` 169 Hash hexutil.Bytes `json:"hash"` 170 Meta Metadata `json:"meta"` 171 } 172 SignDataResponse struct { 173 Approved bool `json:"approved"` 174 Password string 175 } 176 NewAccountRequest struct { 177 Meta Metadata `json:"meta"` 178 } 179 NewAccountResponse struct { 180 Approved bool `json:"approved"` 181 Password string `json:"password"` 182 } 183 ListRequest struct { 184 Accounts []Account `json:"accounts"` 185 Meta Metadata `json:"meta"` 186 } 187 ListResponse struct { 188 Accounts []Account `json:"accounts"` 189 } 190 Message struct { 191 Text string `json:"text"` 192 } 193 StartupInfo struct { 194 Info map[string]interface{} `json:"info"` 195 } 196 ) 197 198 var ErrRequestDenied = errors.New("Request denied") 199 200 //NewSignerAPI创建了一个新的可用于帐户管理的API。 201 //kslocation指定存储受密码保护的private的目录 202 //创建新帐户时生成的键。 203 //nousb禁用支持硬件设备所需的USB支持,如 204 //Ledger和Trezor。 205 func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abidb *AbiDb, lightKDF bool) *SignerAPI { 206 var ( 207 backends []accounts.Backend 208 n, p = keystore.StandardScryptN, keystore.StandardScryptP 209 ) 210 if lightKDF { 211 n, p = keystore.LightScryptN, keystore.LightScryptP 212 } 213 //支持基于密码的帐户 214 if len(ksLocation) > 0 { 215 backends = append(backends, keystore.NewKeyStore(ksLocation, n, p)) 216 } 217 if !noUSB { 218 //启动用于分类帐硬件钱包的USB集线器 219 if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil { 220 log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err)) 221 } else { 222 backends = append(backends, ledgerhub) 223 log.Debug("Ledger support enabled") 224 } 225 //启动Trezor硬件钱包的USB集线器 226 if trezorhub, err := usbwallet.NewTrezorHub(); err != nil { 227 log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err)) 228 } else { 229 backends = append(backends, trezorhub) 230 log.Debug("Trezor support enabled") 231 } 232 } 233 return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...), ui, NewValidator(abidb)} 234 } 235 236 //list返回签名者管理的钱包集。每个钱包都可以包含 237 //多个帐户。 238 func (api *SignerAPI) List(ctx context.Context) (Accounts, error) { 239 var accs []Account 240 for _, wallet := range api.am.Wallets() { 241 for _, acc := range wallet.Accounts() { 242 acc := Account{Typ: "Account", URL: wallet.URL(), Address: acc.Address} 243 accs = append(accs, acc) 244 } 245 } 246 result, err := api.UI.ApproveListing(&ListRequest{Accounts: accs, Meta: MetadataFromContext(ctx)}) 247 if err != nil { 248 return nil, err 249 } 250 if result.Accounts == nil { 251 return nil, ErrRequestDenied 252 253 } 254 return result.Accounts, nil 255 } 256 257 //新建创建新的密码保护帐户。私钥受保护 258 //给定的密码。用户负责备份存储的私钥 259 //在密钥库位置中,创建此API时指定了THA。 260 func (api *SignerAPI) New(ctx context.Context) (accounts.Account, error) { 261 be := api.am.Backends(keystore.KeyStoreType) 262 if len(be) == 0 { 263 return accounts.Account{}, errors.New("password based accounts not supported") 264 } 265 resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}) 266 267 if err != nil { 268 return accounts.Account{}, err 269 } 270 if !resp.Approved { 271 return accounts.Account{}, ErrRequestDenied 272 } 273 return be[0].(*keystore.KeyStore).NewAccount(resp.Password) 274 } 275 276 //logdiff记录传入(原始)事务和从签名者返回的事务之间的差异。 277 //如果修改了事务,它还返回“true”,以便可以将签名者配置为不允许 278 //请求的用户界面修改 279 func logDiff(original *SignTxRequest, new *SignTxResponse) bool { 280 modified := false 281 if f0, f1 := original.Transaction.From, new.Transaction.From; !reflect.DeepEqual(f0, f1) { 282 log.Info("Sender-account changed by UI", "was", f0, "is", f1) 283 modified = true 284 } 285 if t0, t1 := original.Transaction.To, new.Transaction.To; !reflect.DeepEqual(t0, t1) { 286 log.Info("Recipient-account changed by UI", "was", t0, "is", t1) 287 modified = true 288 } 289 if g0, g1 := original.Transaction.Gas, new.Transaction.Gas; g0 != g1 { 290 modified = true 291 log.Info("Gas changed by UI", "was", g0, "is", g1) 292 } 293 if g0, g1 := big.Int(original.Transaction.GasPrice), big.Int(new.Transaction.GasPrice); g0.Cmp(&g1) != 0 { 294 modified = true 295 log.Info("GasPrice changed by UI", "was", g0, "is", g1) 296 } 297 if v0, v1 := big.Int(original.Transaction.Value), big.Int(new.Transaction.Value); v0.Cmp(&v1) != 0 { 298 modified = true 299 log.Info("Value changed by UI", "was", v0, "is", v1) 300 } 301 if d0, d1 := original.Transaction.Data, new.Transaction.Data; d0 != d1 { 302 d0s := "" 303 d1s := "" 304 if d0 != nil { 305 d0s = common.ToHex(*d0) 306 } 307 if d1 != nil { 308 d1s = common.ToHex(*d1) 309 } 310 if d1s != d0s { 311 modified = true 312 log.Info("Data changed by UI", "was", d0s, "is", d1s) 313 } 314 } 315 if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 { 316 modified = true 317 log.Info("Nonce changed by UI", "was", n0, "is", n1) 318 } 319 return modified 320 } 321 322 //signTransaction对给定的事务进行签名,并将其作为json和rlp编码的形式返回 323 func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) { 324 var ( 325 err error 326 result SignTxResponse 327 ) 328 msgs, err := api.validator.ValidateTransaction(&args, methodSelector) 329 if err != nil { 330 return nil, err 331 } 332 333 req := SignTxRequest{ 334 Transaction: args, 335 Meta: MetadataFromContext(ctx), 336 Callinfo: msgs.Messages, 337 } 338 //工艺批准 339 result, err = api.UI.ApproveTx(&req) 340 if err != nil { 341 return nil, err 342 } 343 if !result.Approved { 344 return nil, ErrRequestDenied 345 } 346 //记录用户界面对签名请求所做的更改 347 logDiff(&req, &result) 348 var ( 349 acc accounts.Account 350 wallet accounts.Wallet 351 ) 352 acc = accounts.Account{Address: result.Transaction.From.Address()} 353 wallet, err = api.am.Find(acc) 354 if err != nil { 355 return nil, err 356 } 357 //将字段转换为实际事务 358 var unsignedTx = result.Transaction.toTransaction() 359 360 //要签名的是从UI返回的那个 361 signedTx, err := wallet.SignTxWithPassphrase(acc, result.Password, unsignedTx, api.chainID) 362 if err != nil { 363 api.UI.ShowError(err.Error()) 364 return nil, err 365 } 366 367 rlpdata, err := rlp.EncodeToBytes(signedTx) 368 response := ethapi.SignTransactionResult{Raw: rlpdata, Tx: signedTx} 369 370 //最后,将签名的Tx发送到UI 371 api.UI.OnApprovedTx(response) 372 //…和外部呼叫者 373 return &response, nil 374 375 } 376 377 //sign计算以太坊ECDSA签名: 378 //keccack256(“\x19ethereum签名消息:\n”+len(消息)+消息) 379 // 380 //注:生成的签名符合secp256k1曲线r、s和v值, 381 //由于遗产原因,V值将为27或28。 382 // 383 //用于计算签名的密钥用给定的密码解密。 384 // 385 //https://github.com/ethereum/go-ethereum/wiki/management-apis个人签名 386 func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) { 387 sighash, msg := SignHash(data) 388 //我们在查询是否有账户之前提出请求,以防止 389 //通过API进行帐户枚举 390 req := &SignDataRequest{Address: addr, Rawdata: data, Message: msg, Hash: sighash, Meta: MetadataFromContext(ctx)} 391 res, err := api.UI.ApproveSignData(req) 392 393 if err != nil { 394 return nil, err 395 } 396 if !res.Approved { 397 return nil, ErrRequestDenied 398 } 399 //查找包含请求签名者的钱包 400 account := accounts.Account{Address: addr.Address()} 401 wallet, err := api.am.Find(account) 402 if err != nil { 403 return nil, err 404 } 405 //集合用钱包签名数据 406 signature, err := wallet.SignHashWithPassphrase(account, res.Password, sighash) 407 if err != nil { 408 api.UI.ShowError(err.Error()) 409 return nil, err 410 } 411 signature[64] += 27 //根据黄纸将V从0/1转换为27/28 412 return signature, nil 413 } 414 415 //ecrecover返回用于创建签名的帐户的地址。 416 //注意,此功能与ETH标志和个人标志兼容。因此,它恢复了 417 //地址: 418 //hash=keccak256(“\x19ethereum签名消息:\n”$消息长度$消息) 419 //addr=ecrecover(哈希,签名) 420 // 421 //注意,签名必须符合secp256k1曲线r、s和v值,其中 422 //由于遗留原因,V值必须是27或28。 423 // 424 //https://github.com/ethereum/go-ethereum/wiki/management-apis个人\u-ecrecover 425 func (api *SignerAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { 426 if len(sig) != 65 { 427 return common.Address{}, fmt.Errorf("signature must be 65 bytes long") 428 } 429 if sig[64] != 27 && sig[64] != 28 { 430 return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") 431 } 432 sig[64] -= 27 //将黄纸V从27/28转换为0/1 433 hash, _ := SignHash(data) 434 rpk, err := crypto.SigToPub(hash, sig) 435 if err != nil { 436 return common.Address{}, err 437 } 438 return crypto.PubkeyToAddress(*rpk), nil 439 } 440 441 //signhash是一个帮助函数,用于计算给定消息的哈希 442 //安全地用于计算签名。 443 // 444 //哈希计算为 445 //keccak256(“\x19ethereum签名消息:\n”$消息长度$消息)。 446 // 447 //这将为已签名的消息提供上下文,并防止对事务进行签名。 448 func SignHash(data []byte) ([]byte, string) { 449 msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) 450 return crypto.Keccak256([]byte(msg)), msg 451 } 452 453 //export以Web3密钥库格式返回与给定地址关联的加密私钥。 454 func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) { 455 res, err := api.UI.ApproveExport(&ExportRequest{Address: addr, Meta: MetadataFromContext(ctx)}) 456 457 if err != nil { 458 return nil, err 459 } 460 if !res.Approved { 461 return nil, ErrRequestDenied 462 } 463 //查找包含请求签名者的钱包 464 wallet, err := api.am.Find(accounts.Account{Address: addr}) 465 if err != nil { 466 return nil, err 467 } 468 if wallet.URL().Scheme != keystore.KeyStoreScheme { 469 return nil, fmt.Errorf("Account is not a keystore-account") 470 } 471 return ioutil.ReadFile(wallet.URL().Path) 472 } 473 474 //import尝试在本地密钥库中导入给定的keyjson。keyjson数据应为 475 //以Web3密钥库格式。它将使用给定的密码短语解密keyjson,并在成功时 476 //解密它将使用给定的新密码短语加密密钥,并将其存储在密钥库中。 477 func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage) (Account, error) { 478 be := api.am.Backends(keystore.KeyStoreType) 479 480 if len(be) == 0 { 481 return Account{}, errors.New("password based accounts not supported") 482 } 483 res, err := api.UI.ApproveImport(&ImportRequest{Meta: MetadataFromContext(ctx)}) 484 485 if err != nil { 486 return Account{}, err 487 } 488 if !res.Approved { 489 return Account{}, ErrRequestDenied 490 } 491 acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, res.OldPassword, res.NewPassword) 492 if err != nil { 493 api.UI.ShowError(err.Error()) 494 return Account{}, err 495 } 496 return Account{Typ: "Account", URL: acc.URL, Address: acc.Address}, nil 497 }