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