Skip to content

Latest commit

 

History

History
104 lines (71 loc) · 4.73 KB

File metadata and controls

104 lines (71 loc) · 4.73 KB

Nice Pearl Canary

Medium

Unbounded Growth of participants[profileId] Array

Summary and Impact

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

The ReputationMarket smart contract exhibits an unbounded growth vulnerability within the participants[profileId] array. Specifically, every time a user buys votes, their address is appended to the participants array without any mechanism to remove or manage existing entries. Over time, especially for popular profiles, this array can grow excessively large.

Impact:

  • Gas Consumption: As the participants array grows indefinitely, any future on-chain operations that involve iterating over this array will become increasingly gas-intensive. This can lead to transactions exceeding block gas limits, rendering certain functionalities unusable.
  • Performance Degradation: Operations dependent on the size of the participants array will suffer in performance, potentially affecting user experience and contract reliability.
  • Future Vulnerabilities: An oversized array may introduce unforeseen vulnerabilities or make the contract susceptible to denial-of-service (DoS) attacks, where malicious actors exploit the high gas costs to disrupt contract functionality.

This vulnerability is classified as Medium because, while it does not directly result in financial loss or immediate contract failure, it poses significant risks to the scalability and future usability of the contract.


Vulnerability Details

The vulnerability stems from the unbounded growth of the participants[profileId] array within the ReputationMarket contract. Each time a user interacts with the market (e.g., buying votes), their address is added to the participants array without any checks to prevent duplicates or mechanisms to remove addresses that no longer hold votes.

Code Snippet

// ReputationMarket.sol

// Adding a new participant
participants[profileId].push(msg.sender);
isParticipant[profileId][msg.sender] = true;

These lines are located within functions that handle vote purchases, such as buyVotes.

Test Code Snippet

function testUnboundedParticipantGrowth() public {
    // Step 1: Create a new market for mockProfileId
    vm.deal(address(this), 1 ether);
    reputationMarket.createMarketWithConfig{value: 0.2 ether}(0);

    // Step 2: Have user1 buy votes -> This should add user1 to participants
    vm.deal(user1, 1 ether);
    vm.startPrank(user1);
    reputationMarket.buyVotes{value: 0.01 ether}(mockProfileId, true, 1, 1);
    vm.stopPrank();

    // Step 3: Have user2 buy votes -> This should add user2
    vm.deal(user2, 1 ether);
    vm.startPrank(user2);
    reputationMarket.buyVotes{value: 0.01 ether}(mockProfileId, true, 1, 1);
    vm.stopPrank();

    // Step 4: Have user3 buy & then sell
    vm.deal(user3, 1 ether);
    vm.startPrank(user3);
    reputationMarket.buyVotes{value: 0.01 ether}(mockProfileId, false, 1, 1);
    // Sell everything
    reputationMarket.sellVotes(mockProfileId, false, 1, 0); 
    vm.stopPrank();

    // Check participant count
    uint256 participantCount = reputationMarket.getParticipantCount(mockProfileId);
    console.log("Participant Count after user1, user2, user3 (who sold all):", participantCount);

    // Assert that participantCount is 3
    assertEq(participantCount, 3, "Participant array should have 3 addresses total.");
}

Explanation:

  1. Market Creation: A new reputation market is created for a mock profile ID.
  2. User Interactions:
    • user1 buys votes, resulting in their address being added to the participants array.
    • user2 similarly buys votes, adding their address.
    • user3 buys and then sells all their votes. Despite selling, their address remains in the participants array.
  3. Verification: The test asserts that the participants array contains all three users, demonstrating that even after selling all votes, participants are not removed, leading to unbounded growth.

Tools Used

  • Manual Review
  • Foundry

Recommendations

To mitigate the unbounded growth of the participants[profileId] array, the following strategies should be implemented:

Participant Removal Mechanism:

  • Implement functionality to remove a participant from the participants array when they no longer hold any votes. This can be triggered when a user sells all their votes.
  • Considerations:
    • Removing elements from an array in Solidity can be gas-intensive. To optimize, consider using a mapping with an enumerable set or indexing system.
    • Alternatively, maintain an off-chain record of participants and avoid on-chain enumerations entirely.