geth是go-ethereum中最主要的一个命令行工具,也是各种网络的接入点,支持全节点和轻节点模式,其它程序也可以通过暴露的JSON RPC接口调用访问以太访网络。
// geth整体初始化
func init() {
// 命令/行为,如果用户没有输入其它子命令,就调用这个字段指向的函数
app.Action = geth
app.HideVersion = true // we have a command to print the version
app.Copyright = "Copyright 2013-2017 The go-ethereum Authors"
// 所有支持的子命令
app.Commands = []cli.Command{
// See chaincmd.go:
initCommand,
importCommand,
exportCommand,
copydbCommand,
removedbCommand,
dumpCommand,
monitorCommand,
// 账号管理
accountCommand,
walletCommand,
consoleCommand,
attachCommand,
javascriptCommand,
makecacheCommand,
makedagCommand,
versionCommand,
bugCommand,
licenseCommand,
// See config.go
dumpConfigCommand,
}
// 通过sort函数对cli的所有子命令基于首字母进行排序
sort.Sort(cli.CommandsByName(app.Commands))
// 所有能够解析的options
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, whisperFlags...)
// 在所有命令执行之前调用的函数
app.Before = func(ctx *cli.Context) error {
// 设置用于当前程序的CPU上限
runtime.GOMAXPROCS(runtime.NumCPU())
if err := debug.Setup(ctx); err != nil {
return err
}
// 启动专门监控协程收集正在运行的进程的各种指标,3s一次
go metrics.CollectProcessMetrics(3 * time.Second)
// 配置指定的网络(主网或者其它的测试网络)
utils.SetupNetwork(ctx)
return nil
}
app.After = func(ctx *cli.Context) error {
debug.Exit()
// 重置终端
console.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)
}
}
// 如果没有指定其它子命令,那么geth就是默认的系统入口
// 主要是根据提供的参数创建一个默认的节点
// 以阻塞的方式来运行节点,直到节点被终止
func geth(ctx *cli.Context) error {
// 创建一个节点
node := makeFullNode(ctx)
// 启动节点
startNode(ctx, node)
// 等待,直到节点停止
node.Wait()
return nil
}
// 创建一个节点
// 先创建节点,然后注册各种服务
func makeFullNode(ctx *cli.Context) *node.Node {
// 根据配置创建node
stack, cfg := makeConfigNode(ctx)
// 把以太客户客户端添加到stack中
utils.RegisterEthService(stack, &cfg.Eth)
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
utils.RegisterDashboardService(stack, &cfg.Dashboard)
}
// whisper主要用于加密通讯
shhEnabled := enableWhisper(ctx)
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DeveloperFlag.Name)
if shhEnabled || shhAutoEnabled {
if ctx.GlobalIsSet(utils.WhisperMaxMessageSizeFlag.Name) {
cfg.Shh.MaxMessageSize = uint32(ctx.Int(utils.WhisperMaxMessageSizeFlag.Name))
}
if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {
cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)
}
// 注册ssh服务
utils.RegisterShhService(stack, &cfg.Shh)
}
// 根据命令行参数以及一些特殊的配置来创建一个node
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
// 加载默认配置
cfg := gethConfig{
Eth: eth.DefaultConfig,
Shh: whisper.DefaultConfig,
Node: defaultNodeConfig(),
Dashboard: dashboard.DefaultConfig,
}
if file := ctx.GlobalString(configFileFlag.Name); file != "" {
if err := loadConfig(file, &cfg); err != nil {
utils.Fatalf("%v", err)
}
}
utils.SetNodeConfig(ctx, &cfg.Node)
// 创建一个新的节点
stack, err := node.New(&cfg.Node)
if err != nil {
utils.Fatalf("Failed to create the protocol stack: %v", err)
}
// 将与eth相关的命令应用于配置
utils.SetEthConfig(ctx, stack, &cfg.Eth)
if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
}
// 设置ssh配置
utils.SetShhConfig(ctx, stack, &cfg.Shh)
// 设置dashboard配置
utils.SetDashboardConfig(ctx, &cfg.Dashboard)
return stack, cfg
}
整个启动过程其实就是在解析参数,然后创建、启动节点,再把服务注入到节点中。所有与以太坊相关的功能都是以服务的形式存在。