交互工具
以太坊提供了Geth客户端用于管理API,我们可以在终端输入geth help查看其具体使用方法:
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相关的代码文件结构如下所示:
├─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的使用说明:
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$
可以看到这里的基本格式为:
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)就行
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:命令行执行命令如下
./geth account new
Step 2:进入到go-ethereum-1.10.2\\\\cmd\\\\geth\\\\main.go的init函数构造一个APP对象,然后通过代码配置app对象的行为,并提供必要的回调函数
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\\”
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
Step 4:之后进入到accountcmd.go解析account命令及其二级子命令new
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
// 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来创建账户、验证并存储账户:
// 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并不是私钥也不是助记词,而是将私钥通过钱包密码加密得来的,所以说如果我们得到了钱包密码,那么我们就得到了私钥)的生成方式,
// 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
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并使用传入的随机参数通过椭圆曲线加密算法来生成一个私钥:
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根据私钥来生成公钥,然后根据公钥来生成地址参数并将其返回:
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写文件进去
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格式化:
// 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进行加密处理:
// 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来写文件,在这里会严格设置秘钥存储目录的权限:
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函数中检查使用最初设置的密码是否可以对秘钥文件(临时)进行解密,如果可以则将其进行更名存储:
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\\”来使用~
账户操作
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
交互模式
./geth console
查看节点信息:
>admin.nodeInfo
获取数据库目录信息:
>admin.datadir
获取远程节点列表:
admin.peers
跟多指令可以访问以下连接进行查看:
http://cw.hubwiz.com/card/c/geth-rpc-api/
数据库类
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
钱包操作
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为例对其使用进行了简易演示,后面我们将对公链接口设计进行分析~
原创文章,作者:七芒星实验室,如若转载,请注明出处:https://www.sudun.com/ask/34155.html