Nice Pearl Canary
Medium
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.
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.
// 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
.
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:
- Market Creation: A new reputation market is created for a mock profile ID.
- User Interactions:
user1
buys votes, resulting in their address being added to theparticipants
array.user2
similarly buys votes, adding their address.user3
buys and then sells all their votes. Despite selling, their address remains in theparticipants
array.
- 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.
- Manual Review
- Foundry
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.