code-423n4 / 2024-07-karak-validation

0 stars 0 forks source link

Precision Loss in Effective Balance Conversion #340

Closed c4-bot-1 closed 2 months ago

c4-bot-1 commented 2 months ago

Lines of code

https://github.com/code-423n4/2024-07-karak/blob/main/src/entities/BeaconProofsLib.sol#L137

Vulnerability details

Issue Description

In the BeaconProofsLib contract, there's a potential for precision loss when converting the effective balance from gwei to wei, particularly for large balance values.

Current Implementation:

https://github.com/code-423n4/2024-07-karak/blob/main/src/entities/BeaconProofsLib.sol#L137

function getEffectiveBalanceWei(bytes32[] memory validatorFields) internal pure returns (uint256) {
    return uint256(fromLittleEndianUint64(validatorFields[BALANCE_IDX])) * 1 gwei;
}

issue

While this conversion is generally accurate, it may lead to precision loss for very large balance values due to the limitations of uint64 when converted to wei (which requires higher precision).

Impact

Suggested Solution

  1. Consider using a higher precision type (like uint256) throughout the balance calculation process.
  2. Implement a safe multiplication function to handle the gwei to wei conversion, ensuring no overflow occurs.

Proposed Code Change:

function getEffectiveBalanceWei(bytes32[] memory validatorFields) internal pure returns (uint256) {
    uint256 balanceGwei = uint256(fromLittleEndianUint64(validatorFields[BALANCE_IDX]));
    return balanceGwei * 1e9; // 1 gwei = 10^9 wei
}

Poc

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import "forge-std/Test.sol";
import "../src/entitites/BeaconProofs.sol";

contract BeaconProofsTest is Test {
    using BeaconProofs for bytes32[];

    function testEffectiveBalanceConversionPrecisionLoss() public {
        bytes32[] memory validatorFields = new bytes32[](8);
        uint64 maxUint64 = type(uint64).max;

        // Set the balance to max uint64 value (18,446,744,073,709,551,615)
        validatorFields[BeaconProofs.BALANCE_IDX] = bytes32(uint256(maxUint64));

        uint64 balanceGwei = BeaconProofs.fromLittleEndianUint64(validatorFields[BeaconProofs.BALANCE_IDX]);

        uint256 expectedWei = uint256(maxUint64) * 1e9;

        uint256 actualWei = BeaconProofs.getEffectiveBalanceWei(validatorFields);

        console.log("Balance in Gwei:", balanceGwei);
        console.log("Expected Wei:", expectedWei);
        console.log("Actual Wei:", actualWei);
        console.log("Difference:", expectedWei - actualWei);

        assertEq(actualWei, expectedWei, "Precision loss detected in effective balance conversion");
    }
}

Assessed type

Math