Skip to content

Latest commit

 

History

History
102 lines (76 loc) · 4.16 KB

File metadata and controls

102 lines (76 loc) · 4.16 KB

Cool Mango Bobcat

Medium

Unbounded Growth in Participant Tracking Array Leads to Gas Inefficiency and Storage Bloat

Summary

The ReputationMarket contract implements an append-only data structure for tracking market participants through mapping(uint256 => address[]) public participants. The lack of cleanup mechanism in this implementation creates an unbounded growth pattern where the array of participants for each market continuously expands, even after participants have exited their positions.

The impact manifests through mounting gas inefficiency as the participant list grows, making operations that iterate through the array increasingly expensive. Additionally, the permanent storage requirements expand with each new participant, creating a storage bloat issue that persists even after participants exit their positions.

While this doesn't present immediate security risks, it creates long-term scalability issues and potential gas optimization problems.

Proof of Concept

The issue is evident in the contract's participant tracking implementation:

https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L133

// profileId => participant address
// append only; don't bother removing. Use isParticipant to check if they've sold all their votes.
mapping(uint256 => address[]) public participants;

// profileId => participant => isParticipant
mapping(uint256 => mapping(address => bool)) public isParticipant;

When a new participant enters a market:

https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L474

// In buyVotes:
if (!isParticipant[profileId][msg.sender]) {
    participants[profileId].push(msg.sender);
    isParticipant[profileId][msg.sender] = true;
}

Even when a participant sells all their votes, they remain in the array:

https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L559

// In sellVotes:
votesOwned[msg.sender][profileId].votes[isPositive ? TRUST : DISTRUST] -= votesToSell;
// No removal from participants array

This creates a monotonic growth pattern where the array size only increases, storage costs grow linearly, and any iteration over participants becomes progressively more expensive.

Recommended mitigation steps

The mitigation strategy should focus on efficient data structure management while maintaining necessary functionality. A pagination pattern can be implemented for accessing participant data:

function getParticipants(uint256 profileId, uint256 offset, uint256 limit) 
    public 
    view 
    returns (address[] memory) {
    uint256 end = Math.min(offset + limit, participants[profileId].length);
    address[] memory result = new address[](end - offset);
    for (uint256 i = offset; i < end; i++) {
        result[i - offset] = participants[profileId][i];
    }
    return result;
}

For more comprehensive data management, a doubly linked list structure could be implemented to allow participant removal:

struct Participant {
    address addr;
    uint256 next;
    uint256 prev;
}

mapping(uint256 => mapping(uint256 => Participant)) participants;

In cases where historical participant data isn't critical, a bounded array with replacement mechanism could be employed:

function addParticipant(uint256 profileId, address participant) internal {
    if (participants[profileId].length >= MAX_PARTICIPANTS) {
        // Replace oldest inactive participant
        for (uint256 i = 0; i < participants[profileId].length; i++) {
            address oldParticipant = participants[profileId][i];
            if (!hasActivePosition(profileId, oldParticipant)) {
                participants[profileId][i] = participant;
                return;
            }
        }
    } else {
        participants[profileId].push(participant);
    }
}

The implementation choice should balance data access requirements with gas efficiency and storage optimization, considering the expected scale of market participation and the specific needs for participant data access patterns.