前言

从这一节开始,我将开始以太坊代码全覆盖讲解,讲解的流程是:

  • 以太坊程序入口
  • 基本框架
  • 以太坊协议
  • 发送一笔交易后发生了什么
  • 启动挖矿
  • 以太坊共识
  • p2p 网络

阅读本系列文章,将默认读者具备一定的程序基础,并对 Go 语言特性有一定的了解。如有需要,请自行翻阅 Go 语言相关文档。go 语言中文网点击这里

话不多说,现在开始。

一、以太坊程序入口

1.1 main 函数

以太坊的主程是编译出来的 geth 程序运行时,程序入口跟其他的高级语言一样,都是从 main 函数进入。

进入 main 函数路径:go-ethereum/cmd/geth/main.go ,找到 main 函数。

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

从这里,我们就已经进入了阅读以太坊源码的主流程中。main 函数很简单,执行 app.Run 函数,如果函数返回错误,输出错误,并退出。

熟悉其他主流语言的你看到这里就会想了,main 中没有 app 这个变量,难道它是全局变量?你没有猜错,不过 go 语言更复杂一点,它类似 C++/ Java / Python 语言的集合体,这些先不管。找到 app 对象。

var (...app = utils.NewApp(gitCommit, "the go-ethereum command line interface")
)

点进去进去看 utils.NewApp() 方法是怎么创建 app 对象的,

// NewApp creates an app with sane defaults.
func NewApp(gitCommit, usage string) *cli.App {app := cli.NewApp()app.Name = filepath.Base(os.Args[0])app.Author = ""//app.Authors = nilapp.Email = ""app.Version = params.VersionWithMetaif len(gitCommit) >= 8 {app.Version += "-" + gitCommit[:8]}app.Usage = usagereturn app
}

我们看到,NewApp 方法调用的是 cli.NewApp() 方法,先创建一个cli.App 类型的指针对象,完了给该对象成员赋值,例如:指定 app 的名字是函数执行时的第一个参数,即 geth,还有指定程序的版本号等等,最后函数返回。

我们继续点进去看 cli.NewApp() 是怎么创建一个 app 对象的

// NewApp creates a new cli Application with some reasonable defaults for Name,
// Usage, Version and Action.
func NewApp() *App {return &App{Name:         filepath.Base(os.Args[0]),HelpName:     filepath.Base(os.Args[0]),Usage:        "A new cli application",UsageText:    "",Version:      "0.0.0",BashComplete: DefaultAppComplete,Action:       helpCommand.Action,Compiled:     compileTime(),Writer:       os.Stdout,}
}

函数直接返回的就是一个 App 结构体类型的指针,而 App 结构体在 gopkg.in/urfave/cli.v1 包中定义,它是 app 应用程序的一系列封装,具体的用法我们不展开,在这里为了深入了解 Ethereum 以太坊协议,我们稍微看下 App 结构体的几个主要成员。

1.2 App 结构体对象

// App is the main structure of a cli application. It is recommended that
// an app be created with the cli.NewApp() function
type App struct {Name stringHelpName string...BashComplete BashCompleteFuncBefore BeforeFuncAfter AfterFuncAction interface{}...Writer io.WriterErrWriter io.WriterMetadata map[string]interface{}ExtraInfo func() map[string]stringCustomAppHelpTemplate stringdidSetup bool
}

App 结构体定义如上,我们关注的是其中的4个重要成员:

  • BashComplete :匿名函数类型,可以称之为函数执行器,定义了 bash 执行的行为
  • Before :匿名函数类型,前置函数执行器,定义了 App 对象的 Action 执行之前的行为
  • After :匿名函数类型,后置函数执行器,定义了 App 对象的 Action 执行之后的行为
  • Action :接口类型,App 对象运行的真正执行者

我们知道,以太坊编译出来的二进制文件 geth 可以通过命令行向程序传参,BashComplete 定义了解析命令行参数的行为方式。有兴趣的同学可以关注。

App 对象的执行体由 Action 成员指向的匿名函数定义, Before 成员定义了应用启动之前的初始化操作,而 After 成员定义了应用程序执行完成后的一些行为,值得注意的是,即使程序 panic 了,该成员指向的函数也会执行。

1.3 Before & After

app.Before = func(ctx *cli.Context) error {runtime.GOMAXPROCS(runtime.NumCPU())if err := debug.Setup(ctx); err != nil {return err}// Start system runtime metrics collectiongo metrics.CollectProcessMetrics(3 * time.Second)utils.SetupNetwork(ctx)return nil
}

匿名函数赋值给 app.Before ,函数先调用 runtime.GOMAXPROCS() 方法设置最大处理器的数量,然后启动 debug 相关初始化设置,例如:初始化 logging 设置,检查是否启用 trace 和 cpuprofile 标识,用来判断是否启动 profiling,tracing,pprof 服务等,主要是用来 debug Ethereum 程序性能的。

我们回头来看一下,App.Before 成员指向的函数,其实是跟以太坊协议无关的,主要是做了个全局的初始化设置,是为了跟踪日志,分析内存使用情况和 CPU 使用情况等。接着往下看。

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

app.After 成员用来停止 debug 服务,重置终端模式。其他的就没做什么了。

1.4 App.Action 成员

前面说到,Action 成员是个接口类型的,它代表着 application 运行的真正实体,我们看下它的赋值

func init() {// Initialize the CLI app and start Gethapp.Action = geth...
}

也就是说,Action 成员被初始化 geth 命令行应用程序,它是以太坊主进程函数。

1.5 运行一个应用 app.Run

整个以太坊服务的启动从 main 函数中 app.Run() 开始。点进去:

// Run is the entry point to the cli app. Parses the arguments slice and routes
// to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {a.Setup()......if a.After != nil {defer func() {if afterErr := a.After(context); afterErr != nil {if err != nil {err = NewMultiError(err, afterErr)} else {err = afterErr}}}()}if a.Before != nil {beforeErr := a.Before(context)if beforeErr != nil {ShowAppHelp(context)HandleExitCoder(beforeErr)err = beforeErrreturn err}}args := context.Args()if args.Present() {name := args.First()c := a.Command(name)if c != nil {return c.Run(context)}}if a.Action == nil {a.Action = helpCommand.Action}// Run default Actionerr = HandleAction(a.Action, context)HandleExitCoder(err)return err
}

函数先给 app 对象设置初始值,完了做一些其他的事情,我们暂时不用去关心。
紧接着,判断 app.After 是否为空,如果不空,函数结束后执行 After 方法,因为这是整个程序的唯一主入口,所以,就像前面说的,即使这后面哪里 panic 了,仍然会执行。
然后判断 app.Before 是否为空,如果不空,就执行 Before 方法。
最后判断 a.Action 是否为空,如果为空的话,赋个默认的 helpCommond 值,调用 handleAction 方法来运行 Action ,前面也说了,这里的 action 非空,值为 geth。最终 handleAction 将 contex 作为 geth 的参数并运行该函数。函数执行的结果返回 error 被 HandleExitCoder 函数解析,如果返回错误将错误结果输出到终端。Run 函数返回 error 结束。

1.5 总结

至此,我们简单的看了下,以太坊服务 geth 是在哪里启动的,在它启动之前和启动之后都做了什么,以及以太坊应用程序怎么 Run 。

如果你对 Go 语言有一定了解,你就知道,它其实是一个很精简而高效的语言:它不支持无谓的开销,任何声明而未使用的变量都会在编译期报错,有很多类似 Python 一样的特性。

而我感觉以太坊将这些特性发挥的很好,定义一些全局变量,定义好命令行参数对应解析和取值,然后一个 app.run() 方法启动进程,完了等待进程退出就行了,代码简洁而漂亮。

以太坊源码分析(2)——以太坊APP对象相关推荐

  1. java以太坊源码分析_以太坊区块链Java(EthereumJ)学习笔记:区块链结构-Go语言中文社区...

    本文对EthereumJ的区块链相关的代码做一个简单的介绍. 以太坊区块链 以太坊区块链是在Bitcoin区块链的基础上发展起来的.区块链的数据结构既保留了Bitcoin区块链验证数据的真实性和完整性 ...

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

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

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

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

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

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

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

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

  6. go-ethereum-code-analysis 以太坊源码分析

    分析go-ethereum的过程,我希望从依赖比较少的底层技术组件开始,慢慢深入到核心逻辑. 目录 go-ethereum代码阅读环境搭建 以太坊黄皮书 符号索引 rlp源码解析 trie源码分析 e ...

  7. 以太坊源码分析之随心笔记

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 以太坊索引 table.go 定期随机选取一些节点找他们要他们的节点,放到本地,也就是一个随机找节点的table 里头的 ...

  8. 46.以太坊源码分析(46)p2p-peer.go源码分析

    nat是网络地址转换的意思. 这部分的源码比较独立而且单一,这里就暂时不分析了. 大家了解基本的功能就行了. nat下面有upnp和pmp两种网络协议. upnp的应用场景(pmp是和upnp类似的协 ...

  9. 基于tutk方案的p2p源码_以太坊源码分析--p2p节点发现

    p2p(peer to peer)负责以太坊节点间的通信,主要包括底层节点发现(discover)和上层协议运行两大块,本文主要描述其中节点发现部分的实现 数据结构 节点发现功能主要涉及 Server ...

最新文章

  1. oracle do date,Oracle to_date函数的使用
  2. __stdcall、__cdecl 、CALLBACK 几种函数修饰符
  3. Nginx隐藏PHP入口文件index.php
  4. ef 排序string转int_Java排序算法——基数排序(Radix Sort)
  5. 配置的android版本,Android SDK配置(V3.0.0及以上版本)
  6. SQL Server2005 日期字段与字符串比较的怪异问题
  7. AdventureWorks2012.mdf的使用
  8. 【Get深一度】信号处理(三)——3db带宽
  9. 《一个会写诗的程序员》 东海光剑
  10. Auto CAD中“旋转”命令怎么使用?
  11. 设计模式五:原型模式
  12. C语言 IP地址的转换
  13. 全球及中国电子级无水氟化氢发展动态与未来需求趋势预测报告2021~2026年
  14. 2017 iOS最新面试题汇总(二)
  15. ATV 开发 二 、内置gtvs
  16. 光时域反射仪 具备哪些功能 推荐哪个品牌
  17. 击石乃有火:华为云的生长逻辑
  18. window10安装深度linux双系统,转:【史上最详细】win10下的Deepin双系统安装小白教程-论坛-深度科技...
  19. 学习笔记(77):R语言入门基础-判断逻辑
  20. 【stm32f103】USART TX发送实现(寄存器版)

热门文章

  1. Java学习第一周 equals的含义 equals与==区别
  2. 几种 中文字符集编码规范的关系Unicode ,gb2312 , cp936 ,GBK,GB18030
  3. FLINK 基于1.15.2的Java开发-使用AggregateFunction解决以天为单位诸如PV、UV等统计的实时计算
  4. 2023全国大学生英语竞赛C类试卷
  5. 全国大学英语竞赛备赛一定要看
  6. 2021-2027全球与中国陶瓷喷雾干燥设备市场现状及未来发展趋势
  7. 如何让你的抖音快速涨粉,抖音涨粉不妨试试这六招
  8. iOS 黑白屏滤镜视图层级穿透原理
  9. 为Gerrit Code review添加verified标签
  10. mongodb启动成功连不上_mongoDB启动服务提示已存在连接的问题