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