cosmosSDK代码架构

App

一条基于cosmosSDK的链,就是一个App,App是最顶层单元,负责实现ABCI接口。App以module方式管理。

Tendermint通过ABCI和App通讯,核心的接口如下:

  • CheckTx。验证tx,成功则加入交易池。
  • BeginBlock。执行Block之前调用。
  • DeliverTx。执行Block时调用,对每个tx遍历调用。
  • EndBlock。执行Block结束后调用。
  • Commit。存储落盘。

二者具体关系如图:

-------App-------- 

     a b c i

----Tendermint-----

也就是说,Tendermint调用App,App实现ABCI对应的接口,ABCI接口的执行流程,形成了App的核心逻辑。

其中CosmosSDK提供了一个baseApp,编写了基础的核心逻辑,用户的自定义App往往继承自baseApp。

App以module方式管理,包括以下核心组件:

  • codec。编码。
  • store。存储。
  • keeper。各个模块的具体的功能。
  • anteHandler。验证交易的有效性,被CheckTx和DeliverTx调用。
  • router。各个交易执行的具体路由。
  • module manager。模块管理者。

Module

App中,通过module来管理各个功能模块,比如evm是一个module,ibc是另一个module。代码一般存放在x目录下。

一个module的组成有以下核心组件:

  • keeper。keeper操作store,对外提供模块的具体功能。
  • store。本模块相关的k,v数据库。
  • handler。提供msg(tx)的执行入口。
  • module。提供module级别的对外接口,如module名字,beginBlock, endBlock等函数,module级别的借口往往会调用具体的keeper接口。

需要注意的时,module中可能会使用到别的module的keeper。cosmos采用的方式是在app中创建和管理所有keeper,然后在创建module时,把对应的keeper分配出去。

因此module和keeper并不是严格的上下层级关系,而是一种引用关系,即module中包含keeper,但该keeper是由app创建,而不是module内部创建的。

Module manager

所有module会注册在App的moduleManager中。module manager是一个module的集合。作用是统一调用module级别的接口,如beginBlock, endBlock等函数。

app在执行业务流程,比如执行beginBlock的时候,会调用app的beginBlocker。

app.beginBlocker会调用module manager中的BeginBlock函数,然后遍历所有的module,调用对应的BeginBlock函数,如下所示:

// BeginBlock performs begin block functionality for all modules. It creates a
// child context with an event manager to aggregate events emitted from all
// modules.
func (m *Manager) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
    ctx = ctx.WithEventManager(sdk.NewEventManager())

    for _, moduleName := range m.OrderBeginBlockers {
       module, ok := m.Modules[moduleName].(BeginBlockAppModule)
       if ok {
          module.BeginBlock(ctx, req)
       }
    }

    return abci.ResponseBeginBlock{
       Events: ctx.EventManager().ABCIEvents(),
    }
}

Keeper

keeper是module的下属结构。但由于module中可能会使用到别的module的keeper。cosmos采用的方式是在app中创建和管理所有keeper,然后在创建module时,把对应的keeper分配出去。

因此module和keeper并不是严格的上下层级关系,而是一种引用关系,即module中包含keeper,但该keeper是由app创建,而不是module内部创建的。

anteHandler

anterhandler并不是由module管理的,代码一般不在x目录下,而是单独的ante目录。负责在交易被执行之前,验证交易的有效性,被CheckTx和DeliverTx调用。

anteHandler虽然不是module管理,个人认为是历史原因。一开始的设计认为anteHandle是公共功能,不需要按照module管理。但随着cosmosSDK的发展,anteHandler其实也一定程度按照module划分。

比如在NewAnteHandler代码中,会区分EVMAnteHandler, CosmosAnteHandler。

switch typeURL := opts[0].GetTypeUrl(); typeURL {
case "/ethermint.evm.v1.ExtensionOptionsEthereumTx":
    // handle as *evmtypes.MsgEthereumTx    
    anteHandler = newEVMAnteHandler(options)
case "/ethermint.types.v1.ExtensionOptionsWeb3Tx":
    // handle as normal Cosmos SDK tx, except signature is checked for EIP712 representation    
    anteHandler = newLegacyCosmosAnteHandlerEip712(options)
case "/ethermint.types.v1.ExtensionOptionDynamicFeeTx":
    // cosmos-sdk tx with dynamic fee extension    
    anteHandler = newCosmosAnteHandler(options)
default:
    return ctx, errorsmod.Wrapf(
       errortypes.ErrUnknownExtensionOptions,
       "rejecting tx with unsupported extension option: %s", typeURL,
    )
}

router和handler

交易的具体执行在各个module中,在module中,会有一个handler文件,提供一个NewHandler接口,用于处理具体的tx(msg),如下:

// NewHandler returns a handler for Ethermint type messages.func NewHandler(server types.MsgServer) sdk.Handler {
    return func(ctx sdk.Context, msg sdk.Msg) (result *sdk.Result, err error) {
       ctx = ctx.WithEventManager(sdk.NewEventManager())

       switch msg := msg.(type) {
       case *types.MsgEthereumTx:
          res, err := server.EthereumTx(sdk.WrapSDKContext(ctx), msg)
          return sdk.WrapServiceResult(ctx, res, err)
       case *types.MsgUpdateParams:
          res, err := server.UpdateParams(sdk.WrapSDKContext(ctx), msg)
          return sdk.WrapServiceResult(ctx, res, err)
       default:
          err := errorsmod.Wrapf(errortypes.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg)
          return nil, err       }
    }
}

这些handler会通过moduleManager注册在App的router中,在baseApp执行交易时(runMsgs)时,会取出具体的handler,处理交易,如下:

for i, msg := range msgs {
    // match message route    msgRoute := msg.Route()
    handler := app.router.Route(msgRoute)
    if handler == nil {
       return sdk.ErrUnknownRequest("unrecognized message type: " + msgRoute).Result()
    }

    var msgResult sdk.Result    
    ...
 }

在cosmos把编码从amino升级到proto的时候,router的处理原理一样,但是注册方式发生了变化,通过module的函数注册:

func (am AppModule) RegisterServices(cfg module.Configurator) {
    types.RegisterMsgServer(cfg.MsgServer(), am.keeper)
    ...
}

module的入口也不是handler文件,而是msg_server文件,通过在tx.proto中定义handler和消息类型,最终会在router中保存到msg_server的route。

Store

App中的store采用一种父子关系,有一个总的store,可以理解为数据库,然后基于该store可以创建子store,可以立即为一个个的表,keeper操作具体的子store,并且在最后abci commit的时候,提交总store。

在baseApp初始化时,会创建一个总store,这是store的总入口:

func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore {
    return rootmulti.NewStore(db, log.NewNopLogger())
}

根据总入口,可以创建一个个的子store,每个store对应一个key,在app中会有一个key的列表,根据key列表初始化子store:

keys := sdk.NewKVStoreKeys(
    // SDK keys    authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey,
    distrtypes.StoreKey, slashingtypes.StoreKey,
    govtypes.StoreKey, paramstypes.StoreKey, upgradetypes.StoreKey,
    evidencetypes.StoreKey, capabilitytypes.StoreKey, consensusparamtypes.StoreKey,
    feegrant.StoreKey, authzkeeper.StoreKey, crisistypes.StoreKey,
    // ibc keys    ibcexported.StoreKey, ibctransfertypes.StoreKey,
    // ica keys    icahosttypes.StoreKey,
    // ethermint keys    evmtypes.StoreKey, feemarkettypes.StoreKey,
    // evmos keys    inflationtypes.StoreKey, erc20types.StoreKey, incentivestypes.StoreKey,
    epochstypes.StoreKey, claimstypes.StoreKey, vestingtypes.StoreKey,
    revenuetypes.StoreKey, recoverytypes.StoreKey,
)

// initialize stores
app.MountKVStores(keys)
app.MountTransientStores(tkeys)
app.MountMemoryStores(memKeys)

然后在keeper中,根据对应的key,就可以使用子store:

store := prefix.NewStore(ctx.TransientStore(k.transientKey), types.KeyPrefixTransientBloom)
heightBz := sdk.Uint64ToBigEndian(uint64(ctx.BlockHeight()))
store.Set(heightBz, bloom.Bytes())

最后在abci的commit接口中,保存所有的store:

func (app *BaseApp) Commit() abci.ResponseCommit {
    ....
    // Write the DeliverTx state into branched storage and commit the MultiStore.    // The write to the DeliverTx state writes all 
    state transitions to the root    // MultiStore (app.cms) so when Commit() is called is persists those values.    
    app.deliverState.ms.Write()
    commitID := app.cms.Commit()
 }

Codec

codec负责结构体的编解码。

目前有两种codec, amino和protobuf,amino是比较旧的方式,protobuf是比较新的方式,这里讨论amino。

codec保存结构体的反射信息:

type Codec struct {
    mtx              sync.RWMutex    
    sealed           bool    
    typeInfos        map[reflect.Type]*TypeInfo    
    interfaceInfos   []*TypeInfo    
    concreteInfos    []*TypeInfo    
    disfixToTypeInfo map[DisfixBytes]*TypeInfo    
    nameToTypeInfo   map[string]*TypeInfo
}

在app创建时,会新建一个codec实例:

cdc := amino.NewLegacyAmino()

然后通过module manager,便利所有module,注册codec:

func (bm BasicManager) RegisterCodec(cdc *codec.Codec) {
    for _, b := range bm {
       b.RegisterCodec(cdc)
    }
}

一般会在module的codec.go文件中,实现具体的注册:

init() {    
// Register all Amino interfaces and concrete types 
//on the authz and gov Amino codec so that this can later be    
// used to properly serialize MsgGrant, MsgExec and MsgSubmitProposal instances    
 RegisterLegacyAminoCodec(authzcodec.Amino)   
 RegisterLegacyAminoCodec(govcodec.Amino)    
 RegisterLegacyAminoCodec(groupcodec.Amino)
}


《 “cosmosSDK代码架构” 》 有 5 条评论

  1. augmentin and pregnancy Identifying the bacteria in a sample taken from blood or from infected tissue confirms the diagnosis

  2. WELLBUTRIN XL bupropion hydrochloride, an antidepressant of the aminoketone class, is chemically unrelated to tricyclic, tetracyclic, selective serotonin reuptake inhibitor, or other known antidepressant agents how can i get cheap cytotec online This is not an easy conversation to have

  3. JNCI 104 6 60 452 can you buy cheap cytotec for sale Treatment of congenital heart disease in adults depends on the severity of the heart condition

  4. Pregnancy has a profound effect on the circulatory system dapoxetina comprar online But as with the aromatase enzyme, DHT has a higher affinity for these proteins than testosterone does, so when administered simultaneously the mesterolone will attach to the SHBG and albumin, leaving larger amounts of free testosterone to mediate anabolic activities such as protein synthesis

  5. priligy amazon So I asked him, Do you want to shoot the other one

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

About Me

一位程序员,会弹吉他,喜欢读诗。
有一颗感恩的心,一位美丽的妻子,两个可爱的女儿
mail: geraldlee0825@gmail.com
github: https://github.com/lisuxiaoqi
medium: https://medium.com/@geraldlee0825