Assessment reports>Mitosis>Design>Module: evmengine

Module: evmengine

Description

The evmengine module is provided as part of Omni Network's Octane framework for the purpose of integration with the EVM execution layer.

It interacts with the evmgov and evmvalidator modules that exist within the project.

Unlike the Ethermint project, this module aims to achieve separation between the consensus layer and execution layer by utilizing the Engine API of the execution layer.

Messages

Message: MsgExecutionPayload

This message handler is only executed in Finalize mode where actual state transition occurs.

For the message received as a parameter, this message handler enforces the following elements:

  • Only one of the ExecutionPayloadDeneb and ExecutionPayload fields must be used to transfer the payload to the execution layer.

  • The list of withdrawals in the proposed payload must match the list of eligible withdrawals.

  • The ExecutionWitness field should be empty as it is not used.

  • The fee-recipient address must be valid and approved. (However, the code to check this has not been implemented yet.)

  • Block continuity must be maintained. That is, it must have the previous block hash as the parent block hash, and the current block number must be 1 greater than the previous block number.

  • Block timestamp must be after the most recently executed block and before the current consensus block time.

  • The RandAO value (random field) must match the parent block hash.

Subsequently, it calls the Engine API's engine_newPayloadV4 and engine_forkchoiceUpdatedV3 with the following conditions and roles:

  • engine_newPayloadV4

    • It uses a lock to prevent multiple engine_newPayloadV4 endpoint calls from being processed simultaneously.

    • The passed ExecutableData must be convertible to a valid block.

    • Blocks that have already been processed once are not processed again and return a VALID status.

    • It checks if the block is connected to a previously rejected chain, and if so, increases the hit counter of that block. If it exceeds the threshold, it removes it from the list to give a reprocessing opportunity; otherwise, it also adds the current head to the invalid list. Finally, it sets the parent of the invalid block as the last valid hash (with proof-of-work terminal block exception handling) and returns an "INVALID" status response to block propagation of the invalid chain.

    • It verifies that the parent block of the current block to be executed already exists locally and checks if the timestamp of the current block is greater than that of the parent block.

    • It checks if the execution-layer client is in FullSync state. (If synchronizing, it returns a SYNCING status.)

    • After all these checks, it creates a block based on the inputs received and connects it to the chain (InsertBlockWithoutSetHead).

    • If the execution layer returns an unexpected error or INVALID status when calling this Engine API endpoint, it immediately terminates the message handler, but if the execution layer is still attempting to sync, it repeatedly calls that Engine API endpoint.

  • engine_forkchoiceUpdatedV3

    • The received Withdrawals and BeaconRoot must not be empty.

    • The timestamp of the received block must be later than the cancun and prague fork points specified in the configuration.

    • The received HeadBlockHash must not be a zero hash, and block data matching the block hash must be stored locally or, at least, the block header must be stored in the remote block cache.

    • Block data corresponding to FinalizedBlockHash and SafeBlockHash must be stored locally and be part of the canonical chain.

    • After these verifications are complete, chain reorganization is performed if the designated head is not the head of the canonical chain.

    • It selects one of the determined statuses (VALID, SYNCING, INVALID) and returns a response.

    • If the execution layer returns an unexpected error, returns an INVALID status, or is still attempting to sync when calling this Engine API endpoint, the message handler is immediately terminated.

When the Engine API call to the execution layer is completed, it retrieves the executed events based on the block hash of the execution layer specified in the payload. If there are events defined in advance within the module among those events, they are passed to the evmgov module and the evmvalidator module respectively, and appropriate message handlers are called.

When all these processes are completed, it removes the processed withdrawals and updates the latest block information of the execution layer within the consensus layer.

ABCI++ handlers

Handler: PostFinalize

This is a handler that is called when the current block is finalized and the proposer of the next block is oneself.

It starts building the payload to be submitted to the execution layer at the time of block-proposal generation. It retrieves the latest information at the time the current block is finalized, sets the appropriate timestamp, and sets the Head, Safe, and Finalize block hashes in the execution layer, respectively.

It also configures a payload that includes the list of withdrawals to be processed, Beacon root (app hash), and fee-recipient information, and it calls the engine_forkchoiceUpdatedV3 among the Engine API endpoints located in the execution layer.

Handler: PrepareProposal

This is a handler for preparing the Block to execute when a validator is selected as a block proposer during the Tendermint consensus process.

If preparation exceeds a 10-second time-out, it will propose an empty block.

There should be no transactions arbitrarily inserted by the block proposer. (The ProcessProposal handler that receives the block proposal also thoroughly checks this part to restrict the actions of malicious block proposers.)

It checks if the maximum transaction bytes' size is more than 90MB. (The figure of 90MB may vary depending on one of the configurations of cometBFT, MaxBlockSizeBytes.)

If the payload that was built previously in the PostFinalize handler is not based on the current block height with the latest block information at the time this handler is executed, it rebuilds the payload to be submitted to the execution layer.

If there is no issue with the built payload, it composes a MsgExecutePayload message based on the payload, inserts it as the only transaction in the block, and completes preparations to broadcast the block. (Unlike the original Omni Network code, VoteExtension is not used separately, so an explanation of that part is not included.)

Handler: ProcessProposal

This handler verifies that there is indeed only one transaction included in the block proposal received from the block proposer.

Also, to verify that the previous block has been committed, it checks the vote information of the validators to confirm that more than two thirds of the total voting power has been secured. And it iterates through the transactions included in the block proposal, checks if they are allowed messages, and if so, calls the appropriate message handler. In the current code, the only allowed message is MsgExecutePayload, and it is restricted to be executed only once per block.

Zellic © 2025Back to top ↑