github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/cmd/clef/main.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 //签名者是一个实用程序,可以用来对事务和 26 //任意数据。 27 package main 28 29 import ( 30 "bufio" 31 "context" 32 "crypto/rand" 33 "crypto/sha256" 34 "encoding/hex" 35 "encoding/json" 36 "fmt" 37 "io" 38 "io/ioutil" 39 "os" 40 "os/signal" 41 "os/user" 42 "path/filepath" 43 "runtime" 44 "strings" 45 46 "github.com/ethereum/go-ethereum/cmd/utils" 47 "github.com/ethereum/go-ethereum/common" 48 "github.com/ethereum/go-ethereum/crypto" 49 "github.com/ethereum/go-ethereum/log" 50 "github.com/ethereum/go-ethereum/node" 51 "github.com/ethereum/go-ethereum/rpc" 52 "github.com/ethereum/go-ethereum/signer/core" 53 "github.com/ethereum/go-ethereum/signer/rules" 54 "github.com/ethereum/go-ethereum/signer/storage" 55 "gopkg.in/urfave/cli.v1" 56 ) 57 58 //ExternalApiVersion--请参阅extapi_changelog.md 59 const ExternalAPIVersion = "2.0.0" 60 61 //InternalApiVersion--请参阅intapi_changelog.md 62 const InternalAPIVersion = "2.0.0" 63 64 const legalWarning = ` 65 WARNING! 66 67 Clef is alpha software, and not yet publically released. This software has _not_ been audited, and there 68 are no guarantees about the workings of this software. It may contain severe flaws. You should not use this software 69 unless you agree to take full responsibility for doing so, and know what you are doing. 70 71 TLDR; THIS IS NOT PRODUCTION-READY SOFTWARE! 72 73 ` 74 75 var ( 76 logLevelFlag = cli.IntFlag{ 77 Name: "loglevel", 78 Value: 4, 79 Usage: "log level to emit to the screen", 80 } 81 keystoreFlag = cli.StringFlag{ 82 Name: "keystore", 83 Value: filepath.Join(node.DefaultDataDir(), "keystore"), 84 Usage: "Directory for the keystore", 85 } 86 configdirFlag = cli.StringFlag{ 87 Name: "configdir", 88 Value: DefaultConfigDir(), 89 Usage: "Directory for Clef configuration", 90 } 91 rpcPortFlag = cli.IntFlag{ 92 Name: "rpcport", 93 Usage: "HTTP-RPC server listening port", 94 Value: node.DefaultHTTPPort + 5, 95 } 96 signerSecretFlag = cli.StringFlag{ 97 Name: "signersecret", 98 Usage: "A file containing the password used to encrypt Clef credentials, e.g. keystore credentials and ruleset hash", 99 } 100 dBFlag = cli.StringFlag{ 101 Name: "4bytedb", 102 Usage: "File containing 4byte-identifiers", 103 Value: "./4byte.json", 104 } 105 customDBFlag = cli.StringFlag{ 106 Name: "4bytedb-custom", 107 Usage: "File used for writing new 4byte-identifiers submitted via API", 108 Value: "./4byte-custom.json", 109 } 110 auditLogFlag = cli.StringFlag{ 111 Name: "auditlog", 112 Usage: "File used to emit audit logs. Set to \"\" to disable", 113 Value: "audit.log", 114 } 115 ruleFlag = cli.StringFlag{ 116 Name: "rules", 117 Usage: "Enable rule-engine", 118 Value: "rules.json", 119 } 120 stdiouiFlag = cli.BoolFlag{ 121 Name: "stdio-ui", 122 Usage: "Use STDIN/STDOUT as a channel for an external UI. " + 123 "This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user " + 124 "interface, and can be used when Clef is started by an external process.", 125 } 126 testFlag = cli.BoolFlag{ 127 Name: "stdio-ui-test", 128 Usage: "Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.", 129 } 130 app = cli.NewApp() 131 initCommand = cli.Command{ 132 Action: utils.MigrateFlags(initializeSecrets), 133 Name: "init", 134 Usage: "Initialize the signer, generate secret storage", 135 ArgsUsage: "", 136 Flags: []cli.Flag{ 137 logLevelFlag, 138 configdirFlag, 139 }, 140 Description: ` 141 The init command generates a master seed which Clef can use to store credentials and data needed for 142 the rule-engine to work.`, 143 } 144 attestCommand = cli.Command{ 145 Action: utils.MigrateFlags(attestFile), 146 Name: "attest", 147 Usage: "Attest that a js-file is to be used", 148 ArgsUsage: "<sha256sum>", 149 Flags: []cli.Flag{ 150 logLevelFlag, 151 configdirFlag, 152 signerSecretFlag, 153 }, 154 Description: ` 155 The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of 156 incoming requests. 157 158 Whenever you make an edit to the rule file, you need to use attestation to tell 159 Clef that the file is 'safe' to execute.`, 160 } 161 162 addCredentialCommand = cli.Command{ 163 Action: utils.MigrateFlags(addCredential), 164 Name: "addpw", 165 Usage: "Store a credential for a keystore file", 166 ArgsUsage: "<address> <password>", 167 Flags: []cli.Flag{ 168 logLevelFlag, 169 configdirFlag, 170 signerSecretFlag, 171 }, 172 Description: ` 173 The addpw command stores a password for a given address (keyfile). If you invoke it with only one parameter, it will 174 remove any stored credential for that address (keyfile) 175 `, 176 } 177 ) 178 179 func init() { 180 app.Name = "Clef" 181 app.Usage = "Manage Ethereum account operations" 182 app.Flags = []cli.Flag{ 183 logLevelFlag, 184 keystoreFlag, 185 configdirFlag, 186 utils.NetworkIdFlag, 187 utils.LightKDFFlag, 188 utils.NoUSBFlag, 189 utils.RPCListenAddrFlag, 190 utils.RPCVirtualHostsFlag, 191 utils.IPCDisabledFlag, 192 utils.IPCPathFlag, 193 utils.RPCEnabledFlag, 194 rpcPortFlag, 195 signerSecretFlag, 196 dBFlag, 197 customDBFlag, 198 auditLogFlag, 199 ruleFlag, 200 stdiouiFlag, 201 testFlag, 202 } 203 app.Action = signer 204 app.Commands = []cli.Command{initCommand, attestCommand, addCredentialCommand} 205 206 } 207 func main() { 208 if err := app.Run(os.Args); err != nil { 209 fmt.Fprintln(os.Stderr, err) 210 os.Exit(1) 211 } 212 } 213 214 func initializeSecrets(c *cli.Context) error { 215 if err := initialize(c); err != nil { 216 return err 217 } 218 configDir := c.String(configdirFlag.Name) 219 220 masterSeed := make([]byte, 256) 221 n, err := io.ReadFull(rand.Reader, masterSeed) 222 if err != nil { 223 return err 224 } 225 if n != len(masterSeed) { 226 return fmt.Errorf("failed to read enough random") 227 } 228 err = os.Mkdir(configDir, 0700) 229 if err != nil && !os.IsExist(err) { 230 return err 231 } 232 location := filepath.Join(configDir, "secrets.dat") 233 if _, err := os.Stat(location); err == nil { 234 return fmt.Errorf("file %v already exists, will not overwrite", location) 235 } 236 err = ioutil.WriteFile(location, masterSeed, 0700) 237 if err != nil { 238 return err 239 } 240 fmt.Printf("A master seed has been generated into %s\n", location) 241 fmt.Printf(` 242 This is required to be able to store credentials, such as : 243 * Passwords for keystores (used by rule engine) 244 * Storage for javascript rules 245 * Hash of rule-file 246 247 You should treat that file with utmost secrecy, and make a backup of it. 248 NOTE: This file does not contain your accounts. Those need to be backed up separately! 249 250 `) 251 return nil 252 } 253 func attestFile(ctx *cli.Context) error { 254 if len(ctx.Args()) < 1 { 255 utils.Fatalf("This command requires an argument.") 256 } 257 if err := initialize(ctx); err != nil { 258 return err 259 } 260 261 stretchedKey, err := readMasterKey(ctx) 262 if err != nil { 263 utils.Fatalf(err.Error()) 264 } 265 configDir := ctx.String(configdirFlag.Name) 266 vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) 267 confKey := crypto.Keccak256([]byte("config"), stretchedKey) 268 269 //初始化加密存储 270 configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confKey) 271 val := ctx.Args().First() 272 configStorage.Put("ruleset_sha256", val) 273 log.Info("Ruleset attestation updated", "sha256", val) 274 return nil 275 } 276 277 func addCredential(ctx *cli.Context) error { 278 if len(ctx.Args()) < 1 { 279 utils.Fatalf("This command requires at leaste one argument.") 280 } 281 if err := initialize(ctx); err != nil { 282 return err 283 } 284 285 stretchedKey, err := readMasterKey(ctx) 286 if err != nil { 287 utils.Fatalf(err.Error()) 288 } 289 configDir := ctx.String(configdirFlag.Name) 290 vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) 291 pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) 292 293 //初始化加密存储 294 pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) 295 key := ctx.Args().First() 296 value := "" 297 if len(ctx.Args()) > 1 { 298 value = ctx.Args().Get(1) 299 } 300 pwStorage.Put(key, value) 301 log.Info("Credential store updated", "key", key) 302 return nil 303 } 304 305 func initialize(c *cli.Context) error { 306 //设置记录器以打印所有内容 307 logOutput := os.Stdout 308 if c.Bool(stdiouiFlag.Name) { 309 logOutput = os.Stderr 310 //如果使用stdioui,则无法执行“确认”流 311 fmt.Fprintf(logOutput, legalWarning) 312 } else { 313 if !confirm(legalWarning) { 314 return fmt.Errorf("aborted by user") 315 } 316 } 317 318 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(logOutput, log.TerminalFormat(true)))) 319 return nil 320 } 321 322 func signer(c *cli.Context) error { 323 if err := initialize(c); err != nil { 324 return err 325 } 326 var ( 327 ui core.SignerUI 328 ) 329 if c.Bool(stdiouiFlag.Name) { 330 log.Info("Using stdin/stdout as UI-channel") 331 ui = core.NewStdIOUI() 332 } else { 333 log.Info("Using CLI as UI-channel") 334 ui = core.NewCommandlineUI() 335 } 336 db, err := core.NewAbiDBFromFiles(c.String(dBFlag.Name), c.String(customDBFlag.Name)) 337 if err != nil { 338 utils.Fatalf(err.Error()) 339 } 340 log.Info("Loaded 4byte db", "signatures", db.Size(), "file", c.String("4bytedb")) 341 342 var ( 343 api core.ExternalAPI 344 ) 345 346 configDir := c.String(configdirFlag.Name) 347 if stretchedKey, err := readMasterKey(c); err != nil { 348 log.Info("No master seed provided, rules disabled") 349 } else { 350 351 if err != nil { 352 utils.Fatalf(err.Error()) 353 } 354 vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) 355 356 //生成特定于域的密钥 357 pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) 358 jskey := crypto.Keccak256([]byte("jsstorage"), stretchedKey) 359 confkey := crypto.Keccak256([]byte("config"), stretchedKey) 360 361 //初始化加密存储 362 pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) 363 jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey) 364 configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey) 365 366 //我们有规则文件吗? 367 ruleJS, err := ioutil.ReadFile(c.String(ruleFlag.Name)) 368 if err != nil { 369 log.Info("Could not load rulefile, rules not enabled", "file", "rulefile") 370 } else { 371 hasher := sha256.New() 372 hasher.Write(ruleJS) 373 shasum := hasher.Sum(nil) 374 storedShasum := configStorage.Get("ruleset_sha256") 375 if storedShasum != hex.EncodeToString(shasum) { 376 log.Info("Could not validate ruleset hash, rules not enabled", "got", hex.EncodeToString(shasum), "expected", storedShasum) 377 } else { 378 //初始化规则 379 ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage, pwStorage) 380 if err != nil { 381 utils.Fatalf(err.Error()) 382 } 383 ruleEngine.Init(string(ruleJS)) 384 ui = ruleEngine 385 log.Info("Rule engine configured", "file", c.String(ruleFlag.Name)) 386 } 387 } 388 } 389 390 apiImpl := core.NewSignerAPI( 391 c.Int64(utils.NetworkIdFlag.Name), 392 c.String(keystoreFlag.Name), 393 c.Bool(utils.NoUSBFlag.Name), 394 ui, db, 395 c.Bool(utils.LightKDFFlag.Name)) 396 397 api = apiImpl 398 399 //审计日志 400 if logfile := c.String(auditLogFlag.Name); logfile != "" { 401 api, err = core.NewAuditLogger(logfile, api) 402 if err != nil { 403 utils.Fatalf(err.Error()) 404 } 405 log.Info("Audit logs configured", "file", logfile) 406 } 407 //向服务器注册签名者API 408 var ( 409 extapiURL = "n/a" 410 ipcapiURL = "n/a" 411 ) 412 rpcAPI := []rpc.API{ 413 { 414 Namespace: "account", 415 Public: true, 416 Service: api, 417 Version: "1.0"}, 418 } 419 if c.Bool(utils.RPCEnabledFlag.Name) { 420 421 vhosts := splitAndTrim(c.GlobalString(utils.RPCVirtualHostsFlag.Name)) 422 cors := splitAndTrim(c.GlobalString(utils.RPCCORSDomainFlag.Name)) 423 424 // 425 httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name)) 426 listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts, rpc.DefaultHTTPTimeouts) 427 if err != nil { 428 utils.Fatalf("Could not start RPC api: %v", err) 429 } 430 extapiURL = fmt.Sprintf("http://%s“,httpendpoint) 431 log.Info("HTTP endpoint opened", "url", extapiURL) 432 433 defer func() { 434 listener.Close() 435 log.Info("HTTP endpoint closed", "url", httpEndpoint) 436 }() 437 438 } 439 if !c.Bool(utils.IPCDisabledFlag.Name) { 440 if c.IsSet(utils.IPCPathFlag.Name) { 441 ipcapiURL = c.String(utils.IPCPathFlag.Name) 442 } else { 443 ipcapiURL = filepath.Join(configDir, "clef.ipc") 444 } 445 446 listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI) 447 if err != nil { 448 utils.Fatalf("Could not start IPC api: %v", err) 449 } 450 log.Info("IPC endpoint opened", "url", ipcapiURL) 451 defer func() { 452 listener.Close() 453 log.Info("IPC endpoint closed", "url", ipcapiURL) 454 }() 455 456 } 457 458 if c.Bool(testFlag.Name) { 459 log.Info("Performing UI test") 460 go testExternalUI(apiImpl) 461 } 462 ui.OnSignerStartup(core.StartupInfo{ 463 Info: map[string]interface{}{ 464 "extapi_version": ExternalAPIVersion, 465 "intapi_version": InternalAPIVersion, 466 "extapi_http": extapiURL, 467 "extapi_ipc": ipcapiURL, 468 }, 469 }) 470 471 abortChan := make(chan os.Signal) 472 signal.Notify(abortChan, os.Interrupt) 473 474 sig := <-abortChan 475 log.Info("Exiting...", "signal", sig) 476 477 return nil 478 } 479 480 //splitandtrim拆分由逗号分隔的输入 481 //并修剪子字符串中多余的空白。 482 func splitAndTrim(input string) []string { 483 result := strings.Split(input, ",") 484 for i, r := range result { 485 result[i] = strings.TrimSpace(r) 486 } 487 return result 488 } 489 490 // 491 //持久性要求。 492 func DefaultConfigDir() string { 493 //尝试将数据文件夹放在用户的home目录中 494 home := homeDir() 495 if home != "" { 496 if runtime.GOOS == "darwin" { 497 return filepath.Join(home, "Library", "Signer") 498 } else if runtime.GOOS == "windows" { 499 return filepath.Join(home, "AppData", "Roaming", "Signer") 500 } else { 501 return filepath.Join(home, ".clef") 502 } 503 } 504 //因为我们无法猜测一个稳定的位置,所以返回空的,稍后再处理 505 return "" 506 } 507 508 func homeDir() string { 509 if home := os.Getenv("HOME"); home != "" { 510 return home 511 } 512 if usr, err := user.Current(); err == nil { 513 return usr.HomeDir 514 } 515 return "" 516 } 517 func readMasterKey(ctx *cli.Context) ([]byte, error) { 518 var ( 519 file string 520 configDir = ctx.String(configdirFlag.Name) 521 ) 522 if ctx.IsSet(signerSecretFlag.Name) { 523 file = ctx.String(signerSecretFlag.Name) 524 } else { 525 file = filepath.Join(configDir, "secrets.dat") 526 } 527 if err := checkFile(file); err != nil { 528 return nil, err 529 } 530 masterKey, err := ioutil.ReadFile(file) 531 if err != nil { 532 return nil, err 533 } 534 if len(masterKey) < 256 { 535 return nil, fmt.Errorf("master key of insufficient length, expected >255 bytes, got %d", len(masterKey)) 536 } 537 //创建保管库位置 538 vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterKey)[:10])) 539 err = os.Mkdir(vaultLocation, 0700) 540 if err != nil && !os.IsExist(err) { 541 return nil, err 542 } 543 //!todo,使用kdf拉伸主密钥 544 //拉伸键:=拉伸键(主\键) 545 546 return masterKey, nil 547 } 548 549 //check file是检查文件 550 //*存在 551 //*模式0600 552 func checkFile(filename string) error { 553 info, err := os.Stat(filename) 554 if err != nil { 555 return fmt.Errorf("failed stat on %s: %v", filename, err) 556 } 557 //检查Unix权限位 558 if info.Mode().Perm()&077 != 0 { 559 return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String()) 560 } 561 return nil 562 } 563 564 //确认显示文本并请求用户确认 565 func confirm(text string) bool { 566 fmt.Printf(text) 567 fmt.Printf("\nEnter 'ok' to proceed:\n>") 568 569 text, err := bufio.NewReader(os.Stdin).ReadString('\n') 570 if err != nil { 571 log.Crit("Failed to read user input", "err", err) 572 } 573 574 if text := strings.TrimSpace(text); text == "ok" { 575 return true 576 } 577 return false 578 } 579 580 func testExternalUI(api *core.SignerAPI) { 581 582 ctx := context.WithValue(context.Background(), "remote", "clef binary") 583 ctx = context.WithValue(ctx, "scheme", "in-proc") 584 ctx = context.WithValue(ctx, "local", "main") 585 586 errs := make([]string, 0) 587 588 api.UI.ShowInfo("Testing 'ShowInfo'") 589 api.UI.ShowError("Testing 'ShowError'") 590 591 checkErr := func(method string, err error) { 592 if err != nil && err != core.ErrRequestDenied { 593 errs = append(errs, fmt.Sprintf("%v: %v", method, err.Error())) 594 } 595 } 596 var err error 597 598 _, err = api.SignTransaction(ctx, core.SendTxArgs{From: common.MixedcaseAddress{}}, nil) 599 checkErr("SignTransaction", err) 600 _, err = api.Sign(ctx, common.MixedcaseAddress{}, common.Hex2Bytes("01020304")) 601 checkErr("Sign", err) 602 _, err = api.List(ctx) 603 checkErr("List", err) 604 _, err = api.New(ctx) 605 checkErr("New", err) 606 _, err = api.Export(ctx, common.Address{}) 607 checkErr("Export", err) 608 _, err = api.Import(ctx, json.RawMessage{}) 609 checkErr("Import", err) 610 611 api.UI.ShowInfo("Tests completed") 612 613 if len(errs) > 0 { 614 log.Error("Got errors") 615 for _, e := range errs { 616 log.Error(e) 617 } 618 } else { 619 log.Info("No errors") 620 } 621 622 } 623 624 /* 625 626 627 curl-h“content-type:application/json”-x post--data'“jsonrpc”:“2.0”,“method”:“account_new”,“params”:[“test”],“id”:67“localhost:8550” 628 629 //列出帐户 630 631 curl-i-h“内容类型:application/json”-x post--data'“jsonrpc”:“2.0”,“method”:“account_list”,“params”:[“”],“id”:67”http://localhost:8550/ 632 633 634 //安全端(0x12) 635 //4401A6E4000000000000000000000000000000000000000000000000000000000012 636 637 /供给ABI 638 639 640 641 curl-i-h“content-type:application/json”-x post--data'“jsonrpc”:“2.0”,“method”:“account-signtransaction”,“params”:[“from”:“0x82A2A876D39022B3019932D30CD9C97AD5616813”,“gas”:“0x333”,“gasprice”:“0x123”,“nonce”:“0x 0”,“to”:“0x07A565B7ED7D7A68680A4C162885BEDB695FE0”,“value”:“0x10”,“data”:“0x4401A6E400000000000000000000000000000000000000000”000000000000000000 12“”,“id”:67”http://localhost:8550/ 642 643 644 645 curl-i-h“内容类型:application/json”-x post--数据'“jsonrpc”:“2.0”,“方法”:“帐户符号”,“参数”:[“0x694267f14675d7e1b9494fd8d72fe1755710fa”,“bazonk gaz baz”],“id”:67“http://localhost:8550/ 646 647 648 */ 649