cover_image

剖析 Cosmos SDK 应用程序,Part 1:全节点与核心文件

数与科技
2020年06月15日 09:46
图片


引言


区块链是由人类杰出科技成果组合而成的全新架构,对于生产关系的变革有巨大的潜力。数与科技团队致力于在实践中探索更加开放的加密经济基础设施,我们将逐步拆解主流区块链的底层技术细节,并且尝试将碎片化的区块链知识整理成树状知识图谱分享给社区,也欢迎有兴趣的朋友一起加入。


Cosmos是一套成熟且高可用区块链开发框架,拥有模块化设计、基于Golang开发、支持跨链协议等特点,是目前比较主流的区块链开发框架。通过Cosmos SDK,开发者可以快速构建出属于自己的区块链,高效地进行一系列个性化开发。


本文为Cosmos文档翻译系列第三篇,点击查看往期内容《Cosmos SDK 介绍》



节点客户端


全节点的核心进程是基于SDK包的。网络中的参与者运行此过程以初始化其状态机,与其他全节点连接并在新块进入时更新其状态机。

图片


区块链全节点以二进制形式表示,通常以-d后缀表示守护程序(例如,appd表示app或gaiad表示gaia)。这个二进制文件是通过编译一个简单的代码文件 main.go构建的,main.go 通常位于./cmd/appd/中。此操作通常通过用Makefile编译。


编译了二进制文件,就可以通过运行start命令 来启动节点。此命令功能主要执行三件事:


1. [app.go] 创建了一个状态机实例。


2. 用最新的已知状态初始化状态机,该状态机是从存储在~/ .appd / data文件夹中的db中提取的。此时,状态机的高度为:appBlockHeight。


3. 创建并启动一个新的Tendermint实例。该节点将与对等节点进行连接交换信息。它将从他们那里获取最新的blockHeight,如果它大于本地的appBlockHeight,则重播块以同步到该高度。如果appBlockHeight为0,则该节点从创世开始,并且Tendermint通过ABCI接口向app发送InitChain初始化链命令,从而触发InitChainer。


应用程序核心文件


通常,状态机的核心是在名为app.go的文件中定义的。它主要包含“应用程序的类型定义”和“创建和初始化它”的功能。


1 应用程序的类型定义


在app.go中重要的一个是应用程序的type。它通常由以下部分组成:


baseapp的引用。在app.go中定义的自定义应用程序是baseapp的扩展。当事务由Tendermint发送到应用程序时,app使用baseapp的方法将它们转送到对应的模块。baseapp为应用程序实现了大多数核心逻辑,包括所有的ABCI方法和转送消息逻辑。


一条key链包含整个状态。他是基于 Cosmos SDK 的multistore实现的。每个模块使用multistore的一个或多个存储来存储其状态。可以使用在“ app”类型中声明的特定键来访问这些存储。这些密钥以及`keepers'是Cosmos SDK的 对象功能模型 的核心。


模块keeper的列表。 每个模块 都会抽象定义一个keeper,该keeper 实现模块存储的读写。一个模块的“ keeper”方法可以从其他模块(如果已授权)中调用,这就是为什么它们在应用程序的类型中声明并作为接口导出到其他模块的原因,以便后者只能访问授权的功能。


Codec的引用。应用程序的codec用于序列化和反序列化数据结构以便存储它们,因为存储只能持久化[]bytes。“编解码器”必须是确定性的。默认编解码器为amino


模块管理器的引用。模块管理器是一个对象,其中包含应用程序模块的列表。它简化了与这些模块相关的操作,例如注册routes操作,query route操作或设置各种功能的模块之间顺序执行情况,例如InitChainer操作,BeginBlocke操作和EndBlocker操作


图片


2 构造器函数


此函数构造了以上部分中定义的类型的新应用程序。在应用程的start命令中使用,它必须具有AppCreator签名。


以下是此功能执行的主要操作:

  • 创建初始化一个新的codec实例,并使用 基础模块管理器初始化每个应用程序模块的codec。

  • 使用baseapp实例,编解码器和所有适当的存储键的引用实例化一个新应用程序。

  • 使用每个应用程序模块的NewKeeper功能实例化在应用程序的类型中定义的所有keeper。注意,所有keeper必须以正确的顺序实例化,因为一个模块的NewKeeper可能需要引用另一个模块的keeper。

  • 使用每个应用模块的AppModule 来实例化应用程序的模块管理器

  • 使用模块管理器,初始化应用程序的routes和query route。当事务由Tendermint通过ABCI中继到应用程序时,它使用此处定义的路由被路由到相应模块的 回调handler。同样,当应用程序收到查询时,使用此处定义的查询路由将其路由到适当的模块的querier。

  • 使用模块管理器,注册应用程序的模块的invariants。invariants是在每个块末尾评估的变量(例如token的总供应量)。检查不变式的过程是通过InvariantsRegistry 的特殊模块完成的。invariants应等于模块中定义的预测值。如果该值与预测的值不同,则将触发不变注册表中定义的特殊逻辑(通常会中断链)。这对于确保不会发现任何严重错误并产生难以修复的长期影响非常有用。

  • 使用模块管理器,在每个应用程序的模块 的InitGenesis,BegingBlocker和EndBlocker函数之间设置执行顺序。请注意,并非所有模块都实现这些功能。

  • 模块实现这些功能。

  • 设置其余的应用程序参数:

  • InitChainer于在应用程序首次启动时对其进行初始化。

    • BeginBlocker,EndBlocker:在每个块的开始和结尾处调用。

    • anteHandler:用于处理费用和签名验证。

    • 挂载存储.

  • 返回应用实例.

  • 请注意,此函数仅创建该应用的一个实例,而如果重新启动节点,则状态将从〜/ .appd / data文件夹中保留下来状态加载,如果节点是第一次启动,则从创世文件生成。


查看来自Gaia的示例:

https://github.com/cosmos/gaia/blob/f41a660cdd5bea173139965ade55bd25d1ee3429/app/app.go#L110-L222


InitChainer


InitChainer用于根据创始文件(即创始账户的代币余额)初始化应用程序的状态。当应用程序从Tendermint引擎收到InitChain消息时调用该消息,该消息是在节点以appBlockHeight == 0(即创世)启动。应用程序必须通过SetInitChainer方法设置其constructor中的Initchainer。


通常,InitChainer主要由每个应用程序模块的InitGenesis函数组成。这是通过调用模块管理器的InitGenesis函数来完成的,而模块管理器的InitGenesis函数将依次调用其包含的每个模块的InitGenesis函数。 


请注意,必须使用模块管理器的SetOrderInitGenesis方法设置模块的InitGenesis函数的顺序。这是在 应用程序的构造函数 application-constructor 中完成的,必须在SetInitChainer之前调用SetOrderInitGenesis。

图片


4 BeginBlocker and EndBlocker


该SDK为开发人员提供了在其应用程序中实现自定义代码可能性。这是通过两个名为“ BeginBlocker”和“ EndBlocker”的函数实现的。当应用程序分别从Tendermint引擎接收到BeginBlock和EndBlock消息时,将调用它们,它们分别在每个块的开始和结尾处发生。应用程序必须通过 SetBeginBlocker和SetEndBlocker方法在其 constructor中设置BeginBlocker和EndBlocker。


通常,BeginBlocker和EndBlocker函数主要由每个应用程序模块的BeginBlock和EndBlock函数组成。这是通过调用模块管理器的BeginBlock和EndBlock函数来完成的,而后者又会调用其包含的每个模块的BeginBLock和EndBlock函数。 


请注意,必须分别在模块管理器中使用SetOrderBeginBlock和SetOrderEndBlock方法来设置模块的BegingBlock和EndBlock函数必须调用的顺序。这是通过应用程序的构造函数中的模块管理器完成的,必须调用SetOrderBeginBlock和SetOrderEndBlock方法。在SetBeginBlocker和SetEndBlocker函数之前。


附带说明,请记住特定于应用程序的区块链是确定性的,这一点很重要。开发人员必须注意不要在BeginBlocker或EndBlocker中引入不确定性,还必须注意不要使它们在计算上过于昂贵,因为[gas]不会限制计算代价当调用 BeginBlocker和EndBlocker执行。

图片


5 Register Codec


MakeCodec函数是app.go文件的最后一个重要功能。此函数的目的是使用 RegisterCodec 函数实例化 codeccdc,例如amino初始化SDK的编解码器以及每个应用程序的模块。


为了注册应用程序的模块,MakeCodec函数在ModuleBasics上调用RegisterCodec。ModuleBasics是一个基本管理器,其中列出了应用程序的所有模块。它在init()函数中得到实例化,仅用于注册应用程序模块的非依赖元素(例如编解码器)。要了解有关基本模块管理器的更多信息,请点击这里。

图片




原文链接:

https://docs.cosmos.network/master/basics/app-anatomy.html


作者:cosmos.network




你可能还喜欢:


Cosmos SDK 介绍(上)


Cosmos SDK 介绍(下)




图片

关于我们:

杭州数与科技于2017年在中国杭州成立,由来自EOSIO、COSMOS等开源社区的核心开发者组成。我们致力于在实践中探索主流区块链底层技术,建设开放的信任经济基础设施、消除信任建立成本。



图片
继续滑动看下一个
数与科技
向上滑动看下一个