前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >以太坊交互工具

以太坊交互工具

作者头像
Al1ex
发布2021-07-21 17:46:10
1.7K0
发布2021-07-21 17:46:10
举报
文章被收录于专栏:网络安全攻防网络安全攻防
交互工具

以太坊提供了Geth客户端用于管理API,我们可以在终端输入geth help查看其具体使用方法:

代码语言:javascript
复制
ubuntu@ubuntu:~/geth-linux-amd64$ ./geth --help
NAME:
   geth - the go-ethereum command line interface
   Copyright 2013-2021 The go-ethereum Authors

USAGE:
   geth [options] [command] [command options] [arguments...]
   
VERSION:
   1.10.2-stable-97d11b01
   
COMMANDS:
   account                            Manage accounts
   attach                             Start an interactive JavaScript environment (connect to node)
   console                            Start an interactive JavaScript environment
   db                                 Low level database operations
   dump                               Dump a specific block from storage
   dumpconfig                         Show configuration values
   dumpgenesis                        Dumps genesis block JSON configuration to stdout
   export                             Export blockchain into file
   export-preimages                   Export the preimage database into an RLP stream
   import                             Import a blockchain file
   import-preimages                   Import the preimage database from an RLP stream
   init                               Bootstrap and initialize a new genesis block
   js                                 Execute the specified JavaScript files
   license                            Display license information
   makecache                          Generate ethash verification cache (for testing)
   makedag                            Generate ethash mining DAG (for testing)
   removedb                           Remove blockchain and state databases
   show-deprecated-flags              Show flags that have been deprecated
   snapshot                           A set of commands based on the snapshot
   version                            Print version numbers
   version-check                      Checks (online) whether the current version suffers from any known security vulnerabilities
   wallet                             Manage Ethereum presale wallets
   help, h                            Shows a list of commands or help for one command
   
ETHEREUM OPTIONS:
  --config value                      TOML configuration file
  --datadir value                     Data directory for the databases and keystore (default: "/home/ubuntu/.ethereum")
  --datadir.ancient value             Data directory for ancient chain segments (default = inside chaindata)
  --datadir.minfreedisk value         Minimum free disk space in MB, once reached triggers auto shut down (default = --cache.gc converted to MB, 0 = disabled)
  --keystore value                    Directory for the keystore (default = inside the datadir)
  --usb                               Enable monitoring and management of USB hardware wallets
  --pcscdpath value                   Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
  --networkid value                   Explicitly set network id (integer)(For testnets: use --ropsten, --rinkeby, --goerli instead) (default: 1)
  --mainnet                           Ethereum mainnet
  --goerli                            Görli network: pre-configured proof-of-authority test network
  --rinkeby                           Rinkeby network: pre-configured proof-of-authority test network
  --yolov3                            YOLOv3 network: pre-configured proof-of-authority shortlived test network.
  --ropsten                           Ropsten network: pre-configured proof-of-work test network
  --syncmode value                    Blockchain sync mode ("fast", "full", "snap" or "light") (default: fast)
  --exitwhensynced                    Exits after block synchronisation completes
  --gcmode value                      Blockchain garbage collection mode ("full", "archive") (default: "full")
  --txlookuplimit value               Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain) (default: 2350000)
  --ethstats value                    Reporting URL of a ethstats service (nodename:secret@host:port)
  --identity value                    Custom node name
  --lightkdf                          Reduce key-derivation RAM & CPU usage at some expense of KDF strength
  --whitelist value                   Comma separated block number-to-hash mappings to enforce (<number>=<hash>)
  
LIGHT CLIENT OPTIONS:
  --light.serve value                 Maximum percentage of time allowed for serving LES requests (multi-threaded processing allows values over 100) (default: 0)
  --light.ingress value               Incoming bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited) (default: 0)
  --light.egress value                Outgoing bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited) (default: 0)
  --light.maxpeers value              Maximum number of light clients to serve, or light servers to attach to (default: 100)
  --ulc.servers value                 List of trusted ultra-light servers
  --ulc.fraction value                Minimum % of trusted ultra-light servers required to announce a new head (default: 75)
  --ulc.onlyannounce                  Ultra light server sends announcements only
  --light.nopruning                   Disable ancient light chain data pruning
  --light.nosyncserve                 Enables serving light clients before syncing
  
DEVELOPER CHAIN OPTIONS:
  --dev                               Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
  --dev.period value                  Block period to use in developer mode (0 = mine only if transaction pending) (default: 0)
  
ETHASH OPTIONS:
  --ethash.cachedir value             Directory to store the ethash verification caches (default = inside the datadir)
  --ethash.cachesinmem value          Number of recent ethash caches to keep in memory (16MB each) (default: 2)
  --ethash.cachesondisk value         Number of recent ethash caches to keep on disk (16MB each) (default: 3)
  --ethash.cacheslockmmap             Lock memory maps of recent ethash caches
  --ethash.dagdir value               Directory to store the ethash mining DAGs (default: "/home/ubuntu/.ethash")
  --ethash.dagsinmem value            Number of recent ethash mining DAGs to keep in memory (1+GB each) (default: 1)
  --ethash.dagsondisk value           Number of recent ethash mining DAGs to keep on disk (1+GB each) (default: 2)
  --ethash.dagslockmmap               Lock memory maps for recent ethash mining DAGs
  
TRANSACTION POOL OPTIONS:
  --txpool.locals value               Comma separated accounts to treat as locals (no flush, priority inclusion)
  --txpool.nolocals                   Disables price exemptions for locally submitted transactions
  --txpool.journal value              Disk journal for local transaction to survive node restarts (default: "transactions.rlp")
  --txpool.rejournal value            Time interval to regenerate the local transaction journal (default: 1h0m0s)
  --txpool.pricelimit value           Minimum gas price limit to enforce for acceptance into the pool (default: 1)
  --txpool.pricebump value            Price bump percentage to replace an already existing transaction (default: 10)
  --txpool.accountslots value         Minimum number of executable transaction slots guaranteed per account (default: 16)
  --txpool.globalslots value          Maximum number of executable transaction slots for all accounts (default: 4096)
  --txpool.accountqueue value         Maximum number of non-executable transaction slots permitted per account (default: 64)
  --txpool.globalqueue value          Maximum number of non-executable transaction slots for all accounts (default: 1024)
  --txpool.lifetime value             Maximum amount of time non-executable transaction are queued (default: 3h0m0s)
  
PERFORMANCE TUNING OPTIONS:
  --cache value                       Megabytes of memory allocated to internal caching (default = 4096 mainnet full node, 128 light mode) (default: 1024)
  --cache.database value              Percentage of cache memory allowance to use for database io (default: 50)
  --cache.trie value                  Percentage of cache memory allowance to use for trie caching (default = 15% full mode, 30% archive mode) (default: 15)
  --cache.trie.journal value          Disk journal directory for trie cache to survive node restarts (default: "triecache")
  --cache.trie.rejournal value        Time interval to regenerate the trie cache journal (default: 1h0m0s)
  --cache.gc value                    Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode) (default: 25)
  --cache.snapshot value              Percentage of cache memory allowance to use for snapshot caching (default = 10% full mode, 20% archive mode) (default: 10)
  --cache.noprefetch                  Disable heuristic state prefetch during block import (less CPU and disk IO, more time waiting for data)
  --cache.preimages                   Enable recording the SHA3/keccak preimages of trie keys
  
ACCOUNT OPTIONS:
  --unlock value                      Comma separated list of accounts to unlock
  --password value                    Password file to use for non-interactive password input
  --signer value                      External signer (url or path to ipc file)
  --allow-insecure-unlock             Allow insecure account unlocking when account-related RPCs are exposed by http
  
API AND CONSOLE OPTIONS:
  --ipcdisable                        Disable the IPC-RPC server
  --ipcpath value                     Filename for IPC socket/pipe within the datadir (explicit paths escape it)
  --http                              Enable the HTTP-RPC server
  --http.addr value                   HTTP-RPC server listening interface (default: "localhost")
  --http.port value                   HTTP-RPC server listening port (default: 8545)
  --http.api value                    API's offered over the HTTP-RPC interface
  --http.rpcprefix value              HTTP path path prefix on which JSON-RPC is served. Use '/' to serve on all paths.
  --http.corsdomain value             Comma separated list of domains from which to accept cross origin requests (browser enforced)
  --http.vhosts value                 Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost")
  --ws                                Enable the WS-RPC server
  --ws.addr value                     WS-RPC server listening interface (default: "localhost")
  --ws.port value                     WS-RPC server listening port (default: 8546)
  --ws.api value                      API's offered over the WS-RPC interface
  --ws.rpcprefix value                HTTP path prefix on which JSON-RPC is served. Use '/' to serve on all paths.
  --ws.origins value                  Origins from which to accept websockets requests
  --graphql                           Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well.
  --graphql.corsdomain value          Comma separated list of domains from which to accept cross origin requests (browser enforced)
  --graphql.vhosts value              Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost")
  --rpc.gascap value                  Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite) (default: 25000000)
  --rpc.txfeecap value                Sets a cap on transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap) (default: 1)
  --rpc.allow-unprotected-txs         Allow for unprotected (non EIP155 signed) transactions to be submitted via RPC
  --jspath loadScript                 JavaScript root path for loadScript (default: ".")
  --exec value                        Execute JavaScript statement
  --preload value                     Comma separated list of JavaScript files to preload into the console
  
NETWORKING OPTIONS:
  --bootnodes value                   Comma separated enode URLs for P2P discovery bootstrap
  --discovery.dns value               Sets DNS discovery entry points (use "" to disable DNS)
  --port value                        Network listening port (default: 30303)
  --maxpeers value                    Maximum number of network peers (network disabled if set to 0) (default: 50)
  --maxpendpeers value                Maximum number of pending connection attempts (defaults used if set to 0) (default: 0)
  --nat value                         NAT port mapping mechanism (any|none|upnp|pmp|extip:<IP>) (default: "any")
  --nodiscover                        Disables the peer discovery mechanism (manual peer addition)
  --v5disc                            Enables the experimental RLPx V5 (Topic Discovery) mechanism
  --netrestrict value                 Restricts network communication to the given IP networks (CIDR masks)
  --nodekey value                     P2P node key file
  --nodekeyhex value                  P2P node key as hex (for testing)
  
MINER OPTIONS:
  --mine                              Enable mining
  --miner.threads value               Number of CPU threads to use for mining (default: 0)
  --miner.notify value                Comma separated HTTP URL list to notify of new work packages
  --miner.notify.full                 Notify with pending block headers instead of work packages
  --miner.gasprice value              Minimum gas price for mining a transaction (default: 1000000000)
  --miner.gastarget value             Target gas floor for mined blocks (default: 8000000)
  --miner.gaslimit value              Target gas ceiling for mined blocks (default: 8000000)
  --miner.etherbase value             Public address for block mining rewards (default = first account) (default: "0")
  --miner.extradata value             Block extra data set by the miner (default = client version)
  --miner.recommit value              Time interval to recreate the block being mined (default: 3s)
  --miner.noverify                    Disable remote sealing verification
  
GAS PRICE ORACLE OPTIONS:
  --gpo.blocks value                  Number of recent blocks to check for gas prices (default: 20)
  --gpo.percentile value              Suggested gas price is the given percentile of a set of recent transaction gas prices (default: 60)
  --gpo.maxprice value                Maximum gas price will be recommended by gpo (default: 500000000000)
  
VIRTUAL MACHINE OPTIONS:
  --vmdebug                           Record information useful for VM and contract debugging
  --vm.evm value                      External EVM configuration (default = built-in interpreter)
  --vm.ewasm value                    External ewasm configuration (default = built-in interpreter)
  
LOGGING AND DEBUGGING OPTIONS:
  --fakepow                           Disables proof-of-work verification
  --nocompaction                      Disables db compaction after import
  --verbosity value                   Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (default: 3)
  --vmodule value                     Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. eth/*=5,p2p=4)
  --log.json                          Format logs with JSON
  --log.backtrace value               Request a stack trace at a specific logging statement (e.g. "block.go:271")
  --log.debug                         Prepends log messages with call-site location (file and line number)
  --pprof                             Enable the pprof HTTP server
  --pprof.addr value                  pprof HTTP server listening interface (default: "127.0.0.1")
  --pprof.port value                  pprof HTTP server listening port (default: 6060)
  --pprof.memprofilerate value        Turn on memory profiling with the given rate (default: 524288)
  --pprof.blockprofilerate value      Turn on block profiling with the given rate (default: 0)
  --pprof.cpuprofile value            Write CPU profile to the given file
  --trace value                       Write execution trace to the given file
  
METRICS AND STATS OPTIONS:
  --metrics                           Enable metrics collection and reporting
  --metrics.expensive                 Enable expensive metrics collection and reporting
  --metrics.addr value                Enable stand-alone metrics HTTP server listening interface (default: "127.0.0.1")
  --metrics.port value                Metrics HTTP server listening port (default: 6060)
  --metrics.influxdb                  Enable metrics export/push to an external InfluxDB database
  --metrics.influxdb.endpoint value   InfluxDB API endpoint to report metrics to (default: "http://localhost:8086")
  --metrics.influxdb.database value   InfluxDB database name to push reported metrics to (default: "geth")
  --metrics.influxdb.username value   Username to authorize access to the database (default: "test")
  --metrics.influxdb.password value   Password to authorize access to the database (default: "test")
  --metrics.influxdb.tags value       Comma-separated InfluxDB tags (key/values) attached to all measurements (default: "host=localhost")
  
ALIASED (deprecated) OPTIONS:
  --nousb                             Disables monitoring for and managing USB hardware wallets (deprecated)
  --rpc                               Enable the HTTP-RPC server (deprecated and will be removed June 2021, use --http)
  --rpcaddr value                     HTTP-RPC server listening interface (deprecated and will be removed June 2021, use --http.addr) (default: "localhost")
  --rpcport value                     HTTP-RPC server listening port (deprecated and will be removed June 2021, use --http.port) (default: 8545)
  --rpccorsdomain value               Comma separated list of domains from which to accept cross origin requests (browser enforced) (deprecated and will be removed June 2021, use --http.corsdomain)
  --rpcvhosts value                   Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (deprecated and will be removed June 2021, use --http.vhosts) (default: "localhost")
  --rpcapi value                      API's offered over the HTTP-RPC interface (deprecated and will be removed June 2021, use --http.api)
  
MISC OPTIONS:
  --snapshot                          Enables snapshot-database mode (default = enable)
  --bloomfilter.size value            Megabytes of memory allocated to bloom-filter for pruning (default: 2048)
  --help, -h                          show help
  --override.berlin value             Manually specify Berlin fork-block, overriding the bundled setting (default: 0)
  

COPYRIGHT:
   Copyright 2013-2021 The go-ethereum Authors
   
ubuntu@ubuntu:~/geth-linux-amd64$
源码分析

Geth相关的代码文件结构如下所示:

代码语言:javascript
复制
├─geth
│  │  accountcmd.go        钱包账户相关
│  │  chaincmd.go        链数据相关
│  │  config.go          配置文件相关
│  │  consolecmd.go        console交互模式
│  │  dbcmd.go          数据库操作相关
│  │  main.go          节点启动主程序
│  │  misccmd.go        杂项查询相关
│  │  snapshot.go        snapshot相关
│  │  usage.go          使用模板
│  │  version_check.go      安全版本检查

首先我们来看一下Geth的使用说明:

代码语言:javascript
复制
ubuntu@ubuntu:~/geth-linux-amd64$ ./geth --help
NAME:
   geth - the go-ethereum command line interface

   Copyright 2013-2021 The go-ethereum Authors

USAGE:
   geth [options] [command] [command options] [arguments...]
   
VERSION:
   1.10.2-stable-97d11b01
   
COMMANDS:
   account                            Manage accounts
   attach                             Start an interactive JavaScript environment (connect to node)
   console                            Start an interactive JavaScript environment
   db                                 Low level database operations
   dump                               Dump a specific block from storage
   dumpconfig                         Show configuration values
   dumpgenesis                        Dumps genesis block JSON configuration to stdout
   export                             Export blockchain into file
   export-preimages                   Export the preimage database into an RLP stream
   import                             Import a blockchain file
   import-preimages                   Import the preimage database from an RLP stream
   init                               Bootstrap and initialize a new genesis block
   js                                 Execute the specified JavaScript files
   license                            Display license information
   makecache                          Generate ethash verification cache (for testing)
   makedag                            Generate ethash mining DAG (for testing)
   removedb                           Remove blockchain and state databases
   show-deprecated-flags              Show flags that have been deprecated
   snapshot                           A set of commands based on the snapshot
   version                            Print version numbers
   version-check                      Checks (online) whether the current version suffers from any known security vulnerabilities
   wallet                             Manage Ethereum presale wallets
   help, h                            Shows a list of commands or help for one command
   
ETHEREUM OPTIONS:
  --config value                      TOML configuration file
  --datadir value                     Data directory for the databases and keystore (default: "/home/ubuntu/.ethereum")
.....
  

COPYRIGHT:
   Copyright 2013-2021 The go-ethereum Authors
   
ubuntu@ubuntu:~/geth-linux-amd64$

可以看到这里的基本格式为:

代码语言:javascript
复制
 geth [options] [command] [command options] [arguments...]

下面来看一下go-ethereum-1.10.2\cmd\geth\main.go文件,在该文件中可以看到有两个比较关键的函数:init函数、main函数,熟悉Go语言的都知道init函数与main函数都是Go语言保留的两个函数,当一个文件中出现init函数与main函数时,go语言会自动安装一定顺序先调用所有保留的init函数,之后再调用main函数,在当前文件中的init函数它是第三方包gopkg.in/urfave/cli.v1的实例,其用法是首先构造一个APP对象,然后通过代码配置app对象的行为,并提供必要的回调函数,在运行的时候直接在main函数里面运行app.Run(os.Args)就行

代码语言:javascript
复制
import (
  "fmt"
  "os"
  "sort"
  "strconv"
  "strings"
  "time"

  "github.com/ethereum/go-ethereum/accounts"
  ......
  "gopkg.in/urfave/cli.v1"
)

const (
  clientIdentifier = "geth" // Client identifier to advertise over the network
)

var (
  // Git SHA1 commit hash of the release (set via linker flags)
  gitCommit = ""
  gitDate   = ""
  // The app that holds all commands and flags.
  app = flags.NewApp(gitCommit, gitDate, "the go-ethereum command line interface")
  // flags that configure the node
  nodeFlags = []cli.Flag{
    utils.IdentityFlag,
    utils.UnlockedAccountFlag,
    utils.PasswordFileFlag,
    utils.BootnodesFlag,
    utils.DataDirFlag,
    utils.AncientFlag,
    utils.MinFreeDiskSpaceFlag,
    utils.KeyStoreDirFlag,
    ......
    utils.MinerNotifyFullFlag,
    configFileFlag,
  }

  rpcFlags = []cli.Flag{
    utils.HTTPEnabledFlag,
    ......
    utils.AllowUnprotectedTxs,
  }

  metricsFlags = []cli.Flag{
    utils.MetricsEnabledFlag,
    ......
    utils.MetricsInfluxDBTagsFlag,
  }
)

func init() {
  // Initialize the CLI app and start Geth
  app.Action = geth    
  app.HideVersion = true // we have a command to print the version
  app.Copyright = "Copyright 2013-2021 The go-ethereum Authors"
  app.Commands = []cli.Command{
    // See chaincmd.go:
    initCommand,
    importCommand,
    exportCommand,
    importPreimagesCommand,
    exportPreimagesCommand,
    removedbCommand,
    dumpCommand,
    dumpGenesisCommand,
    // See accountcmd.go:
    accountCommand,
    walletCommand,
    // See consolecmd.go:
    consoleCommand,
    attachCommand,
    javascriptCommand,
    // See misccmd.go:
    makecacheCommand,
    makedagCommand,
    versionCommand,
    versionCheckCommand,
    licenseCommand,
    // See config.go
    dumpConfigCommand,
    // see dbcmd.go
    dbCommand,
    // See cmd/utils/flags_legacy.go
    utils.ShowDeprecated,
    // See snapshot.go
    snapshotCommand,
  }
  sort.Sort(cli.CommandsByName(app.Commands))

  app.Flags = append(app.Flags, nodeFlags...)
  app.Flags = append(app.Flags, rpcFlags...)
  app.Flags = append(app.Flags, consoleFlags...)
  app.Flags = append(app.Flags, debug.Flags...)
  app.Flags = append(app.Flags, metricsFlags...)

  app.Before = func(ctx *cli.Context) error {
    return debug.Setup(ctx)
  }
  app.After = func(ctx *cli.Context) error {
    debug.Exit()
    prompt.Stdin.Close() // Resets terminal mode.
    return nil
  }
}

func main() {
  if err := app.Run(os.Args); err != nil {
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
  }
}

// prepare manipulates memory cache allowance and setups metric system.
// This function should be called before launching devp2p stack.
func prepare(ctx *cli.Context) {
  // If we're running a known preset, log it for convenience.
  switch {
  case ctx.GlobalIsSet(utils.RopstenFlag.Name):
    log.Info("Starting Geth on Ropsten testnet...")

  case ctx.GlobalIsSet(utils.RinkebyFlag.Name):
    log.Info("Starting Geth on Rinkeby testnet...")

  case ctx.GlobalIsSet(utils.GoerliFlag.Name):
    log.Info("Starting Geth on Görli testnet...")

  case ctx.GlobalIsSet(utils.YoloV3Flag.Name):
    log.Info("Starting Geth on YOLOv3 testnet...")

  case ctx.GlobalIsSet(utils.DeveloperFlag.Name):
    log.Info("Starting Geth in ephemeral dev mode...")

  case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name):
    log.Info("Starting Geth on Ethereum mainnet...")
  }
  // If we're a full node on mainnet without --cache specified, bump default cache allowance
  if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
    // Make sure we're not on any supported preconfigured testnet either
    if !ctx.GlobalIsSet(utils.RopstenFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) {
      // Nope, we're really on mainnet. Bump that cache up!
      log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
      ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
    }
  }
  // If we're running a light client on any network, drop the cache to some meaningfully low amount
  if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) {
    log.Info("Dropping default light client cache", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 128)
    ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(128))
  }

  // Start metrics export if enabled
  utils.SetupMetrics(ctx)

  // Start system runtime metrics collection
  go metrics.CollectProcessMetrics(3 * time.Second)
}

// geth is the main entry point into the system if no special subcommand is ran.
// It creates a default node based on the command line arguments and runs it in
// blocking mode, waiting for it to be shut down.
func geth(ctx *cli.Context) error {
  if args := ctx.Args(); len(args) > 0 {
    return fmt.Errorf("invalid command: %q", args[0])
  }

  prepare(ctx)
  stack, backend := makeFullNode(ctx)
  defer stack.Close()

  startNode(ctx, stack, backend)
  stack.Wait()
  return nil
}

上述代码中的init函数内的app.Action表示如果用户没有输入其他的子命令的情况下,会调用这个字段指向的函数,也就是我们的geth函数,这也是我们上一篇文章中为什么直接说"geth"是启动节点时的入口,而非main函数的缘故,同时从上面的geth命令格式可以看出这里的主要交互式命令都是"command"参数,而启动节点的相关参数都是"options"参数,下面我们以创建账户为例进行源码分析:

Step 1:命令行执行命令如下

代码语言:javascript
复制
./geth account new

Step 2:进入到go-ethereum-1.10.2\cmd\geth\main.go的init函数构造一个APP对象,然后通过代码配置app对象的行为,并提供必要的回调函数

代码语言:javascript
复制
func init() {
  // Initialize the CLI app and start Geth
  app.Action = geth
  app.HideVersion = true // we have a command to print the version
  app.Copyright = "Copyright 2013-2021 The go-ethereum Authors"
  app.Commands = []cli.Command{
    // See chaincmd.go:
    initCommand,
    importCommand,
    exportCommand,
    importPreimagesCommand,
    exportPreimagesCommand,
    removedbCommand,
    dumpCommand,
    dumpGenesisCommand,
    // See accountcmd.go:
    accountCommand,
    walletCommand,
    // See consolecmd.go:
    consoleCommand,
    attachCommand,
    javascriptCommand,
    // See misccmd.go:
    makecacheCommand,
    makedagCommand,
    versionCommand,
    versionCheckCommand,
    licenseCommand,
    // See config.go
    dumpConfigCommand,
    // see dbcmd.go
    dbCommand,
    // See cmd/utils/flags_legacy.go
    utils.ShowDeprecated,
    // See snapshot.go
    snapshotCommand,
  }
  sort.Sort(cli.CommandsByName(app.Commands))

  app.Flags = append(app.Flags, nodeFlags...)
  app.Flags = append(app.Flags, rpcFlags...)
  app.Flags = append(app.Flags, consoleFlags...)
  app.Flags = append(app.Flags, debug.Flags...)
  app.Flags = append(app.Flags, metricsFlags...)

  app.Before = func(ctx *cli.Context) error {
    return debug.Setup(ctx)
  }
  app.After = func(ctx *cli.Context) error {
    debug.Exit()
    prompt.Stdin.Close() // Resets terminal mode.
    return nil
  }
}

Step 3:之后在main函数里面运行app.Run(os.Args),这里的传入的参数中command为"account",其对应的参数为"new"

代码语言:javascript
复制
func main() {
  if err := app.Run(os.Args); err != nil {
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
  }
}

Step 4:之后进入到accountcmd.go解析account命令及其二级子命令new

代码语言:javascript
复制
var (
  walletCommand = cli.Command{
    Name:      "wallet",
    Usage:     "Manage Ethereum presale wallets",
    ArgsUsage: "",
    Category:  "ACCOUNT COMMANDS",
    Description: `
    geth wallet import /path/to/my/presale.wallet

will prompt for your password and imports your ether presale account.
It can be used non-interactively with the --password option taking a
passwordfile as argument containing the wallet password in plaintext.`,
    Subcommands: []cli.Command{
      {

        Name:      "import",
        Usage:     "Import Ethereum presale wallet",
        ArgsUsage: "<keyFile>",
        Action:    utils.MigrateFlags(importWallet),
        Category:  "ACCOUNT COMMANDS",
        Flags: []cli.Flag{
          utils.DataDirFlag,
          utils.KeyStoreDirFlag,
          utils.PasswordFileFlag,
          utils.LightKDFFlag,
        },
        Description: `
  geth wallet [options] /path/to/my/presale.wallet

will prompt for your password and imports your ether presale account.
It can be used non-interactively with the --password option taking a
passwordfile as argument containing the wallet password in plaintext.`,
      },
    },
  }

  accountCommand = cli.Command{
    Name:     "account",
    Usage:    "Manage accounts",
    Category: "ACCOUNT COMMANDS",
    Description: `

Manage accounts, list all existing accounts, import a private key into a new
account, create a new account or update an existing account.

It supports interactive mode, when you are prompted for password as well as
non-interactive mode where passwords are supplied via a given password file.
Non-interactive mode is only meant for scripted use on test networks or known
safe environments.

Make sure you remember the password you gave when creating a new account (with
either new or import). Without it you are not able to unlock your account.

Note that exporting your key in unencrypted format is NOT supported.

Keys are stored under <DATADIR>/keystore.
It is safe to transfer the entire directory or the individual keys therein
between ethereum nodes by simply copying.

Make sure you backup your keys regularly.`,
    Subcommands: []cli.Command{
      {
        Name:   "list",
        Usage:  "Print summary of existing accounts",
        Action: utils.MigrateFlags(accountList),
        Flags: []cli.Flag{
          utils.DataDirFlag,
          utils.KeyStoreDirFlag,
        },
        Description: `
Print a short summary of all accounts`,
      },
      {
        Name:   "new",
        Usage:  "Create a new account",
        Action: utils.MigrateFlags(accountCreate),
        Flags: []cli.Flag{
          utils.DataDirFlag,
          utils.KeyStoreDirFlag,
          utils.PasswordFileFlag,
          utils.LightKDFFlag,
        },
        Description: `
    geth account new

Creates a new account and prints the address.

The account is saved in encrypted format, you are prompted for a password.

You must remember this password to unlock your account in the future.

For non-interactive use the password can be specified with the --password flag:

Note, this is meant to be used for testing only, it is a bad idea to save your
password to file or expose in any other way.
`,
      },
      {
        Name:      "update",
        Usage:     "Update an existing account",
        Action:    utils.MigrateFlags(accountUpdate),
        ArgsUsage: "<address>",
        Flags: []cli.Flag{
          utils.DataDirFlag,
          utils.KeyStoreDirFlag,
          utils.LightKDFFlag,
        },
        Description: `
    geth account update <address>

Update an existing account.

The account is saved in the newest version in encrypted format, you are prompted
for a password to unlock the account and another to save the updated file.

This same command can therefore be used to migrate an account of a deprecated
format to the newest format or change the password for an account.

For non-interactive use the password can be specified with the --password flag:

    geth account update [options] <address>

Since only one password can be given, only format update can be performed,
changing your password is only possible interactively.
`,
      },
      {
        Name:   "import",
        Usage:  "Import a private key into a new account",
        Action: utils.MigrateFlags(accountImport),
        Flags: []cli.Flag{
          utils.DataDirFlag,
          utils.KeyStoreDirFlag,
          utils.PasswordFileFlag,
          utils.LightKDFFlag,
        },
        ArgsUsage: "<keyFile>",
        Description: `
    geth account import <keyfile>

Imports an unencrypted private key from <keyfile> and creates a new account.
Prints the address.

The keyfile is assumed to contain an unencrypted private key in hexadecimal format.

The account is saved in encrypted format, you are prompted for a password.

You must remember this password to unlock your account in the future.

For non-interactive use the password can be specified with the -password flag:

    geth account import [options] <keyfile>

Note:
As you can directly copy your encrypted accounts to another ethereum instance,
this import mechanism is not needed when you transfer an account between
nodes.
`,
      },
    },
  }
)

找到对应的处理函数accountCreate

代码语言:javascript
复制
// accountCreate creates a new account into the keystore defined by the CLI flags.
func accountCreate(ctx *cli.Context) error {
  cfg := gethConfig{Node: defaultNodeConfig()}
  // Load config file.
  if file := ctx.GlobalString(configFileFlag.Name); file != "" {
    if err := loadConfig(file, &cfg); err != nil {
      utils.Fatalf("%v", err)
    }
  }
  utils.SetNodeConfig(ctx, &cfg.Node)
  scryptN, scryptP, keydir, err := cfg.Node.AccountConfig()

  if err != nil {
    utils.Fatalf("Failed to read configuration: %v", err)
  }

  password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))

  account, err := keystore.StoreKey(keydir, password, scryptN, scryptP)

  if err != nil {
    utils.Fatalf("Failed to create account: %v", err)
  }
  fmt.Printf("\nYour new key was generated\n\n")
  fmt.Printf("Public address of the key:   %s\n", account.Address.Hex())
  fmt.Printf("Path of the secret key file: %s\n\n", account.URL.Path)
  fmt.Printf("- You can share your public address with anyone. Others need it to interact with you.\n")
  fmt.Printf("- You must NEVER share the secret key with anyone! The key controls access to your funds!\n")
  fmt.Printf("- You must BACKUP your key file! Without the key, it's impossible to access account funds!\n")
  fmt.Printf("- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!\n\n")
  return nil
}

在上面的代码中会首先去加载对应的节点配置信息,之后应用节点配置,然后要求用户输入新账户的密码,然后调用keystore.StoreKey来创建账户、验证并存储账户:

代码语言:javascript
复制
// StoreKey generates a key, encrypts with 'auth' and stores in the given directory
func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) {
  _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
  return a, err
}

func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
  keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
  if err != nil {
    return err
  }
  // Write into temporary file
  tmpName, err := writeTemporaryKeyFile(filename, keyjson)
  if err != nil {
    return err
  }
  if !ks.skipKeyFileVerification {
    // Verify that we can decrypt the file with the given password.
    _, err = ks.GetKey(key.Address, tmpName, auth)
    if err != nil {
      msg := "An error was encountered when saving and verifying the keystore file. \n" +
        "This indicates that the keystore is corrupted. \n" +
        "The corrupted file is stored at \n%v\n" +
        "Please file a ticket at:\n\n" +
        "https://github.com/ethereum/go-ethereum/issues." +
        "The error was : %s"
      //lint:ignore ST1005 This is a message for the user
      return fmt.Errorf(msg, tmpName, err)
    }
  }
  return os.Rename(tmpName, filename)
}

在这里我们顺便来跟踪一下keystore(需要注意这里的keystore并不是私钥也不是助记词,而是将私钥通过钱包密码加密得来的,所以说如果我们得到了钱包密码,那么我们就得到了私钥)的生成方式,

代码语言:javascript
复制
// StoreKey generates a key, encrypts with 'auth' and stores in the given directory
func StoreKey(dir, auth string, scryptN, scryptP int) (accounts.Account, error) {
  _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
  return a, err
}

之后跟进storeNewKey

代码语言:javascript
复制
func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
  key, err := newKey(rand)
  if err != nil {
    return nil, accounts.Account{}, err
  }
  a := accounts.Account{
    Address: key.Address,
    URL:     accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
  }
  if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
    zeroKey(key.PrivateKey)
    return nil, a, err
  }
  return key, a, err
}

在这里会调用netKey并使用传入的随机参数通过椭圆曲线加密算法来生成一个私钥:

代码语言:javascript
复制
func newKey(rand io.Reader) (*Key, error) {
  privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)
  if err != nil {
    return nil, err
  }
  return newKeyFromECDSA(privateKeyECDSA), nil
}

然后在调用newKeyFromECDSA根据私钥来生成公钥,然后根据公钥来生成地址参数并将其返回:

代码语言:javascript
复制
func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
  id, err := uuid.NewRandom()
  if err != nil {
    panic(fmt.Sprintf("Could not create random uuid: %v", err))
  }
  key := &Key{
    Id:         id,
    Address:    crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
    PrivateKey: privateKeyECDSA,
  }
  return key
}

之后调用StoreKey来存储账户信息,在这里会首先对生成的key(结构体类型,包含账户地址、私钥、ID序列)和密码进行一次加密操作,然后调用writeTemporaryKeyFile写文件进去

代码语言:javascript
复制
func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
  keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
  if err != nil {
    return err
  }
  // Write into temporary file
  tmpName, err := writeTemporaryKeyFile(filename, keyjson)
  if err != nil {
    return err
  }
  if !ks.skipKeyFileVerification {
    // Verify that we can decrypt the file with the given password.
    _, err = ks.GetKey(key.Address, tmpName, auth)
    if err != nil {
      msg := "An error was encountered when saving and verifying the keystore file. \n" +
        "This indicates that the keystore is corrupted. \n" +
        "The corrupted file is stored at \n%v\n" +
        "Please file a ticket at:\n\n" +
        "https://github.com/ethereum/go-ethereum/issues." +
        "The error was : %s"
      //lint:ignore ST1005 This is a message for the user
      return fmt.Errorf(msg, tmpName, err)
    }
  }
  return os.Rename(tmpName, filename)
}

加密流程如下,在这里会调用EncryptDataV3来进行关键的加密操作,然后将数据进行JSON格式化:

代码语言:javascript
复制
// EncryptKey encrypts a key using the specified scrypt parameters into a json
// blob that can be decrypted later on.
func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
  keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
  cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
  if err != nil {
    return nil, err
  }
  encryptedKeyJSONV3 := encryptedKeyJSONV3{
    hex.EncodeToString(key.Address[:]),
    cryptoStruct,
    key.Id.String(),
    version,
  }
  return json.Marshal(encryptedKeyJSONV3)
}

EncryptDataV3加密的具体实现代码如下所示,这里以我们输入的密码的作为盐值,然后经过一系列的处理后使用Keccak256进行加密处理:

代码语言:javascript
复制
// Encryptdata encrypts the data given as 'data' with the password 'auth'.
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {

  salt := make([]byte, 32)
  if _, err := io.ReadFull(rand.Reader, salt); err != nil {
    panic("reading from crypto/rand failed: " + err.Error())
  }
  derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
  if err != nil {
    return CryptoJSON{}, err
  }
  encryptKey := derivedKey[:16]

  iv := make([]byte, aes.BlockSize) // 16
  if _, err := io.ReadFull(rand.Reader, iv); err != nil {
    panic("reading from crypto/rand failed: " + err.Error())
  }
  cipherText, err := aesCTRXOR(encryptKey, data, iv)
  if err != nil {
    return CryptoJSON{}, err
  }
  mac := crypto.Keccak256(derivedKey[16:32], cipherText)

  scryptParamsJSON := make(map[string]interface{}, 5)
  scryptParamsJSON["n"] = scryptN
  scryptParamsJSON["r"] = scryptR
  scryptParamsJSON["p"] = scryptP
  scryptParamsJSON["dklen"] = scryptDKLen
  scryptParamsJSON["salt"] = hex.EncodeToString(salt)
  cipherParamsJSON := cipherparamsJSON{
    IV: hex.EncodeToString(iv),
  }

  cryptoStruct := CryptoJSON{
    Cipher:       "aes-128-ctr",
    CipherText:   hex.EncodeToString(cipherText),
    CipherParams: cipherParamsJSON,
    KDF:          keyHeaderKDF,
    KDFParams:    scryptParamsJSON,
    MAC:          hex.EncodeToString(mac),
  }
  return cryptoStruct, nil
}

之后调用writeTemporaryKeyFile来写文件,在这里会严格设置秘钥存储目录的权限:

代码语言:javascript
复制
func writeTemporaryKeyFile(file string, content []byte) (string, error) {
  // Create the keystore directory with appropriate permissions
  // in case it is not present yet.
  const dirPerm = 0700
  if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil {
    return "", err
  }
  // Atomic write: create a temporary hidden file first
  // then move it into place. TempFile assigns mode 0600.
  f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
  if err != nil {
    return "", err
  }
  if _, err := f.Write(content); err != nil {
    f.Close()
    os.Remove(f.Name())
    return "", err
  }
  f.Close()
  return f.Name(), nil
}

最后在StoreKey函数中检查使用最初设置的密码是否可以对秘钥文件(临时)进行解密,如果可以则将其进行更名存储:

代码语言:javascript
复制
  if !ks.skipKeyFileVerification {
    // Verify that we can decrypt the file with the given password.
    _, err = ks.GetKey(key.Address, tmpName, auth)
    if err != nil {
      msg := "An error was encountered when saving and verifying the keystore file. \n" +
        "This indicates that the keystore is corrupted. \n" +
        "The corrupted file is stored at \n%v\n" +
        "Please file a ticket at:\n\n" +
        "https://github.com/ethereum/go-ethereum/issues." +
        "The error was : %s"
      //lint:ignore ST1005 This is a message for the user
      return fmt.Errorf(msg, tmpName, err)
    }
  }
  return os.Rename(tmpName, filename)

step 5:最后会返回账户地址信息、秘钥存储路径,其秘钥文件信息(AES-128加密且加盐处理)、秘钥文件的权限设置如下所示

PS:有安全经验的读者应该会发现这里少了一个关键点——"密码复杂度校验",从而导致用户可以设置弱口令,例如:123456

Geth使用

下面我们简单介绍一下Geth的基本使用,跟多可以在控制台配合"--help"来使用~

账户操作
代码语言:javascript
复制
ubuntu@ubuntu:~/geth-linux-amd64$ ./geth  account --help
NAME:
   geth account - 

Manage accounts, list all existing accounts, import a private key into a new
account, create a new account or update an existing account.

It supports interactive mode, when you are prompted for password as well as
non-interactive mode where passwords are supplied via a given password file.
Non-interactive mode is only meant for scripted use on test networks or known
safe environments.

Make sure you remember the password you gave when creating a new account (with
either new or import). Without it you are not able to unlock your account.

Note that exporting your key in unencrypted format is NOT supported.

Keys are stored under <DATADIR>/keystore.
It is safe to transfer the entire directory or the individual keys therein
between ethereum nodes by simply copying.

Make sure you backup your keys regularly.

USAGE:
   geth account command [command options] [arguments...]

COMMANDS:
     list                             Print summary of existing accounts
     new                              Create a new account
     update                           Update an existing account
     import                           Import a private key into a new account

OPTIONS:
   --help, -h                         show help
交互模式
代码语言:javascript
复制
./geth console

查看节点信息:

代码语言:javascript
复制
>admin.nodeInfo

获取数据库目录信息:

代码语言:javascript
复制
>admin.datadir

获取远程节点列表:

代码语言:javascript
复制
admin.peers

跟多指令可以访问以下连接进行查看:

http://cw.hubwiz.com/card/c/geth-rpc-api/

数据库类
代码语言:javascript
复制
ubuntu@ubuntu:~/geth-linux-amd64$ ./geth db --help
NAME:
   geth db - Low level database operations

USAGE:
   geth db command [command options] [arguments...]

COMMANDS:
     inspect                          Inspect the storage size for each type of data in the database
     stats                            Print leveldb statistics
     compact                          Compact leveldb database. WARNING: May take a very long time
     get                              Show the value of a database key
     delete                           Delete a database key (WARNING: may corrupt your database)
     put                              Set the value of a database key (WARNING: may corrupt your database)
     dumptrie                         Show the storage key/values of a given storage trie

OPTIONS:
   --help, -h                         show help
钱包操作
代码语言:javascript
复制
ubuntu@ubuntu:~/geth-linux-amd64$ ./geth wallet --help
NAME:
   geth wallet - 
    geth wallet import /path/to/my/presale.wallet

will prompt for your password and imports your ether presale account.
It can be used non-interactively with the --password option taking a
passwordfile as argument containing the wallet password in plaintext.

USAGE:
   geth wallet command [command options] [arguments...]

COMMANDS:
   ACCOUNT COMMANDS:
     import                           Import Ethereum presale wallet

OPTIONS:
   --help, -h                         show help
   
ubuntu@ubuntu:~/geth-linux-amd64$
搭私有链

暂略~

文末小结

本篇文章以以太坊公链交互工具Geth为例介绍了公链交互工具命令参数的解析与执行流程,同时以Geth为例对其使用进行了简易演示,后面我们将对公链接口设计进行分析~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 七芒星实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 交互工具
  • 源码分析
  • Geth使用
    • 账户操作
      • 交互模式
        • 数据库类
          • 钱包操作
          • 搭私有链
          • 文末小结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档