Assessment reports>Polyhedra DVN>High findings>Verification procedure lacks minimum confirmations check
Category: Business Logic

Verification procedure lacks minimum confirmations check

High Severity
High Impact
High Likelihood

Description

The _verify function is an essential component of the ZKBridgeOracleV2 contract. It ensures that the parameters of each particular packet that has been transmitted cross-chain are legitimate, and it determines whether those packets can be forwarded to the Receive Ultra-Light Node (ULN).

function _verify(
    bytes32 _blockHash,
    bytes32 _receiptHash,
    uint32 _srcEid,
    address _receiver,
    bytes memory _packetHeader,
    bytes32 _payloadHash
) internal {
    IBlockUpdater blockUpdater = blockUpdaters[_srcEid];
    if (address(blockUpdater) == address(0)) revert UnsupportedUpdater(_srcEid);
    (bool exist, uint256 blockConfirmation) = blockUpdater.checkBlockConfirmation(_blockHash, _receiptHash);
    if (!exist) revert BlockNotSet();
    (address receiverLib,) = layerZeroEndpointV2.getReceiveLibrary(_receiver, _srcEid);
    IReceiveUlnE2(receiverLib).verify(_packetHeader, _payloadHash, uint64(blockConfirmation));
}

In the current implementation, the function checks whether the given _blockHash is valid for the _receiptHash, and if so, it forwards the specific packet on to the Ultra-Light Node.

As per the official LayerZero DVN documentation (point 4) requirements, however, prior to the Ultra-Light Node verification, the _verify function must ensure that the packet has accrued enough block confirmations.

Impact

Lack of an adequate verification procedure might lead to the exclusion of the DVN from the LayerZero Verification layer, as it might propose to verifications packets that have not yet passed enough block confirmations.

Recommendations

We recommend implementing the following checks that ensure all LayerZero standards are respected adequately:

function _verify(
    bytes32 _blockHash,
    bytes32 _receiptHash,
    uint32 _srcEid,
    address _receiver,
    bytes memory _packetHeader,
    bytes32 _payloadHash
) internal {
    // ... 
    (address receiverLib,) = layerZeroEndpointV2.getReceiveLibrary(_receiver, _srcEid);

+    UlnConfig memory ulnConfig = IReceiveUlnE2(receiverLib).getUlnConfig(
+        _receiver,
+        _srcEid
+    );
+    
+    require(
+       blockConfirmations >= ulnConfig.confirmations,
+       "ZkBridgeOracleV2: packet not verified"
+    );

    IReceiveUlnE2(receiverLib).verify(_packetHeader, _payloadHash, uint64(blockConfirmation));
}

Remediation

This issue has been acknowledged by Polyhedra Network, and a fix was implemented in commit 17df5bda.

Zellic © 2025Back to top ↑