geth是我们的go-ethereum最主要的一个命令行工具。 也是我们的各种网络的接入点(主网络main-net 测试网络test-net 和私有网络)。支持运行在全节点模式或者轻量级节点模式。 其他程序可以通过它暴露的JSON RPC调用来访问以太坊网络的功能。
如果什么命令都不输入直接运行geth。 就会默认启动一个全节点模式的节点。 连接到主网络。 我们看看启动的主要流程是什么,涉及到了那些组件。
## 启动的main函数 cmd/geth/main.go
看到main函数一上来就直接运行了。 最开始看的时候是有点懵逼的。 后面发现go语言里面有两个默认的函数,一个是main()函数。一个是init()函数。 go语言会自动按照一定的顺序先调用所有包的init()函数。然后才会调用main()函数。
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
main.go的init函数
app是一个三方包gopkg.in/urfave/cli.v1的实例。 这个三方包的用法大致就是首先构造这个app对象。 通过代码配置app对象的行为,提供一些回调函数。然后运行的时候直接在main函数里面运行 app.Run(os.Args)就行了。
import (
...
"gopkg.in/urfave/cli.v1"
)
var (
app = utils.NewApp(gitCommit, "the go-ethereum command line interface")
// flags that configure the node
nodeFlags = []cli.Flag{
utils.IdentityFlag,
utils.UnlockedAccountFlag,
utils.PasswordFileFlag,
utils.BootnodesFlag,
...
}
rpcFlags = []cli.Flag{
utils.RPCEnabledFlag,
utils.RPCListenAddrFlag,
...
}
whisperFlags = []cli.Flag{
utils.WhisperEnabledFlag,
...
}
)
func init() {
// Initialize the CLI app and start Geth
// Action字段表示如果用户没有输入其他的子命令的情况下,会调用这个字段指向的函数。
app.Action = geth
app.HideVersion = true // we have a command to print the version
app.Copyright = "Copyright 2013-2017 The go-ethereum Authors"
// Commands 是所有支持的子命令
app.Commands = []cli.Command{
// See chaincmd.go:
initCommand,
importCommand,
exportCommand,
removedbCommand,
dumpCommand,
// See monitorcmd.go:
monitorCommand,
// See accountcmd.go:
accountCommand,
walletCommand,
// See consolecmd.go:
consoleCommand,
attachCommand,
javascriptCommand,
// See misccmd.go:
makecacheCommand,
makedagCommand,
versionCommand,
bugCommand,
licenseCommand,
// See config.go
dumpConfigCommand,
}
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 {
runtime.GOMAXPROCS(runtime.NumCPU())
if err := debug.Setup(ctx); err != nil {
return err
}
// Start system runtime metrics collection
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
}
}
如果我们没有输入任何的参数,那么会自动调用geth方法。
// 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.
// 如果没有指定特殊的子命令,那么geth是系统主要的入口。
// 它会根据提供的参数创建一个默认的节点。并且以阻塞的模式运行这个节点,等待着节点被终止。
func geth(ctx *cli.Context) error {
node := makeFullNode(ctx)
startNode(ctx, node)
node.Wait()
return nil
}
makeFullNode函数,
func makeFullNode(ctx *cli.Context) *node.Node {
// 根据命令行参数和一些特殊的配置来创建一个node
stack, cfg := makeConfigNode(ctx)
// 把eth的服务注册到这个节点上面。 eth服务是以太坊的主要的服务。 是以太坊功能的提供者。
utils.RegisterEthService(stack, &cfg.Eth)
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
// Whisper是一个新的模块,用来进行加密通讯的功能。 需要显式的提供参数来启用,或者是处于开发模式。
shhEnabled := enableWhisper(ctx)
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.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)
}
// 注册Shh服务
utils.RegisterShhService(stack, &cfg.Shh)
}
// Add the Ethereum Stats daemon if requested.
if cfg.Ethstats.URL != "" {
// 注册 以太坊的状态服务。 默认情况下是没有启动的。
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
}
// Add the release oracle service so it boots along with node.
// release oracle服务是用来查看客户端版本是否是最新版本的服务。
// 如果需要更新。 那么会通过打印日志来提示版本更新。
// release 是通过智能合约的形式来运行的。 后续会详细讨论这个服务。
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
config := release.Config{
Oracle: relOracle,
Major: uint32(params.VersionMajor),
Minor: uint32(params.VersionMinor),
Patch: uint32(params.VersionPatch),
}
commit, _ := hex.DecodeString(gitCommit)
copy(config.Commit[:], commit)
return release.NewReleaseService(ctx, config)
}); err != nil {
utils.Fatalf("Failed to register the Geth release oracle service: %v", err)
}
return stack
}
makeConfigNode。 这个函数主要是通过配置文件和flag来生成整个系统的运行配置。
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
// Load defaults.
cfg := gethConfig{
Eth: eth.DefaultConfig,
Shh: whisper.DefaultConfig,
Node: defaultNodeConfig(),
}
// Load config file.
if file := ctx.GlobalString(configFileFlag.Name); file != "" {
if err := loadConfig(file, &cfg); err != nil {
utils.Fatalf("%v", err)
}
}
// Apply flags.
utils.SetNodeConfig(ctx, &cfg.Node)
stack, err := node.New(&cfg.Node)
if err != nil {
utils.Fatalf("Failed to create the protocol stack: %v", err)
}
utils.SetEthConfig(ctx, stack, &cfg.Eth)
if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
}
utils.SetShhConfig(ctx, stack, &cfg.Shh)
return stack, cfg
}
RegisterEthService
// RegisterEthService adds an Ethereum client to the stack.
func RegisterEthService(stack *node.Node, cfg *eth.Config) {
var err error
// 如果同步模式是轻量级的同步模式。 那么启动轻量级的客户端。
if cfg.SyncMode == downloader.LightSync {
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return les.New(ctx, cfg)
})
} else {
// 否则会启动全节点
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
fullNode, err := eth.New(ctx, cfg)
if fullNode != nil && cfg.LightServ > 0 {
// 默认LightServ的大小是0 也就是不会启动LesServer
// LesServer是给轻量级节点提供服务的。
ls, _ := les.NewLesServer(fullNode, cfg)
fullNode.AddLesServer(ls)
}
return fullNode, err
})
}
if err != nil {
Fatalf("Failed to register the Ethereum service: %v", err)
}
}
startNode
// startNode boots up the system node and all registered protocols, after which
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner.
func startNode(ctx *cli.Context, stack *node.Node) {
// Start up the node itself
utils.StartNode(stack)
// Unlock any account specifically requested
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
passwords := utils.MakePasswordList(ctx)
unlocks := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
for i, account := range unlocks {
if trimmed := strings.TrimSpace(account); trimmed != "" {
unlockAccount(ctx, ks, trimmed, i, passwords)
}
}
// Register wallet event handlers to open and auto-derive wallets
events := make(chan accounts.WalletEvent, 16)
stack.AccountManager().Subscribe(events)
go func() {
// Create an chain state reader for self-derivation
rpcClient, err := stack.Attach()
if err != nil {
utils.Fatalf("Failed to attach to self: %v", err)
}
stateReader := ethclient.NewClient(rpcClient)
// Open any wallets already attached
for _, wallet := range stack.AccountManager().Wallets() {
if err := wallet.Open(""); err != nil {
log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
}
}
// Listen for wallet event till termination
for event := range events {
switch event.Kind {
case accounts.WalletArrived:
if err := event.Wallet.Open(""); err != nil {
log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
}
case accounts.WalletOpened:
status, _ := event.Wallet.Status()
log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)
if event.Wallet.URL().Scheme == "ledger" {
event.Wallet.SelfDerive(accounts.DefaultLedgerBaseDerivationPath, stateReader)
} else {
event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
}
case accounts.WalletDropped:
log.Info("Old wallet dropped", "url", event.Wallet.URL())
event.Wallet.Close()
}
}
}()
// Start auxiliary services if enabled
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
// Mining only makes sense if a full Ethereum node is running
var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil {
utils.Fatalf("ethereum service not running: %v", err)
}
// Use a reduced number of threads if requested
if threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name); threads > 0 {
type threaded interface {
SetThreads(threads int)
}
if th, ok := ethereum.Engine().(threaded); ok {
th.SetThreads(threads)
}
}
// Set the gas price to the limits from the CLI and start mining
ethereum.TxPool().SetGasPrice(utils.GlobalBig(ctx, utils.GasPriceFlag.Name))
if err := ethereum.StartMining(true); err != nil {
utils.Fatalf("Failed to start mining: %v", err)
}
}
}
总结:
整个启动过程其实就是解析参数。然后创建和启动节点。 然后把服务注入到节点中。 所有跟以太坊相关的功能都是以服务的形式实现的。

网址:http://www.qukuailianxueyuan.io/

欲领取造币技术与全套虚拟机资料

区块链技术交流QQ群:756146052  备注:CSDN

尹成学院微信:备注:CSDN

以太坊源码分析(39)geth启动流程分析相关推荐

  1. php区块链以太坊,兄弟连区块链教程以太坊源码分析CMD深入分析(一)

    兄弟连区块链教程以太坊源码分析CMD深入分析. cmd包分析 cmd下面总共有13个子包,除了util包之外,每个子包都有一个主函数,每个主函数的init方法中都定义了该主函数支持的命令,如 geth ...

  2. 以太坊源码分析(2)——以太坊APP对象

    前言 从这一节开始,我将开始以太坊代码全覆盖讲解,讲解的流程是: 以太坊程序入口 基本框架 以太坊协议 发送一笔交易后发生了什么 启动挖矿 以太坊共识 p2p 网络 阅读本系列文章,将默认读者具备一定 ...

  3. 3 v4 中心节点固定_死磕以太坊源码分析之p2p节点发现

    死磕以太坊源码分析之p2p节点发现 在阅读节点发现源码之前必须要理解kadmilia算法,可以参考:KAD算法详解. 节点发现概述 节点发现,使本地节点得知其他节点的信息,进而加入到p2p网络中. 以 ...

  4. 以太坊源码分析-交易

    以太坊源码分析-交易 机理 先说一点区块链转账的基本概念和流程 用户输入转账的地址和转入的地址和转出的金额 系统通过转出的地址的私钥对转账信息进行签名(用于证明这 笔交易确实有本人进行) 系统对交易信 ...

  5. 以太坊源码阅读5——POW源码分析

    以太坊源码阅读5--POW源码分析 介绍 POW,proof of work,即工作量证明,是著名公bitcoin所采用的共识算法.简单来说,pow就是一个证明,由矿工使用算力进行计算(挖矿),竞争记 ...

  6. kademlia java_死磕以太坊源码分析之Kademlia算法

    死磕以太坊源码分析之Kademlia算法 KAD 算法概述 Kademlia是一种点对点分布式哈希表(DHT),它在容易出错的环境中也具有可证明的一致性和性能.使用一种基于异或指标的拓扑结构来路由查询 ...

  7. 以太坊源码学习(一) 正本清源

    以太坊源码学习(一)正本清源 背景 geth源码一直在不断增加,优化,发展到现在已经非常庞大,第一次看geth源码,会有不小的难度.虽然如此,还是可以从geth仓库的第一个commit开始,这时的代码 ...

  8. 以太坊源码学习 -- EVM

    以太坊源码学习 – EVM 学习文档链接:here 一.虚拟机外 主要功能: 执行前将Transaction类型转化成Message,创建虚拟机(EVM)对象,计算一些Gas消耗,以及执行交易完毕后创 ...

  9. 以太坊源码阅读3——MPT原理

    以太坊源码阅读3--MPT原理 介绍 MPT(Merkel-Patricia Tree),翻译为梅克尔-帕特里夏树 MPT提供了一个基于密码学验证的底层数据结构,用来存储键值对( key-value) ...

  10. 以太坊源码阅读2——RLP编码

    以太坊源码阅读2--RLP编码 RLP介绍 目前网上的资料都是RLP(Recursive Length prefix),叫递归长度前缀编码,但目前源码的doc.go的第20行里面的注释写的是 The ...

最新文章

  1. Python 项目实践二(下载数据)第四篇
  2. linux shell ls 输出存进数组变量
  3. java32位无符号数_用C语言解释32位无符号长单精度IEEE-754浮点数
  4. 《他其实没那么喜欢你》经典台词(2)
  5. WebApi安全性 使用TOKEN+签名验证
  6. PHP接口与抽象类区别:接口用来扩展应用 抽象用来总结归纳
  7. 2008安装完了找不到_7206BEP.进口轴承_玉溪SKF轴承安装指南
  8. weiphp看版本_安装WeiPHP
  9. 流浪猫的故事看质量人员需求
  10. python中string库_Python——string库
  11. 计算机视觉常用图像软件对比和分析
  12. 省市县三级联动插件distpicker
  13. 对拳击“海盗式”打法特点的剖析
  14. 【寒江雪】Unity中调用C++的函数和类
  15. [Linux] 添加清华镜像
  16. 如何在C加加的面向对象写石头剪刀布游戏
  17. 中英文停止词表(stopword)
  18. Python中的单行、多行、中文注释
  19. 使用echarts加载地图
  20. Unity Input的手柄映射

热门文章

  1. vscode新建文件夹
  2. elementUI table表格合并相同的内容
  3. 简单好用的在线P图工具,一定记得收藏
  4. 我国超级计算机历代,《决战崛起——中国超算强国之路》作品研讨会召开
  5. linux下使用VSCode的C++添加json问题。
  6. VSCode 插件大全
  7. 实验三:CART回归决策树python实现(两个测试集)(二)|机器学习
  8. 浅谈共线性的产生以及解决方法(中篇——今生)
  9. 机器学习:多重共线性
  10. Python机器学习个人总结