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