In the RankedChoice::selectWinner
the RankedChoice::_selectPresidentRecursive
, which is recursive, is called to tally up the votes and eliminate the candidate with fewest votes. Since there are no limits to the number of candidates that can be voted on, this function when called can become very gas costly making it susceptible to a denial of service (DoS) attack if maliciuos voters vote on different candidates to make the s_candidateList
array as large as possible.
Even if there is no maliciuos voters the protocol could not handle large scaling of candidates because calling RankedChoice::selectWinner
would become to expensive.
Code
If the RankedChoice::s_candidateList
array becomes to large the RankedChoice::selectWinner
will be very gas costly to call. When calling RankedChoice::selectWinner
function the transaction could run out of gas or even make it to costly to call when the s_candidateList.length
becomes to long. This could make it hard to select a president and start a new election breaking the protocol.
Manual code review
In the code below we first have an election where 10 voters vote on the identical ordered candidates.
Then there is a new election where we have 10 voters vote on 3 different candidates where not a single candidate is the same.
Add this code to the test suit:
When running this test you can clearly see that the last election uses much more gas then the former.
There are few ways to mitigate this vulnerability:
Batch Processing or Iterative Elimination
One of the main reasons for DoS attacks due to high gas consumption is that the selectWinner function might be trying to eliminate all candidates in a single transaction, especially in a recursive function. To avoid this, you can break down the winner selection process into smaller, more manageable steps using batch processing or by converting the process into an iterative loop instead of recursion.
Offloading Vote Tallying to Off-Chain Solutions
Another advanced solution is to offload the vote tallying and candidate elimination process to an off-chain solution, which would then submit the final result to the contract on-chain. This approach is often called hybrid on-chain/off-chain computation.
Commit-Reveal Scheme for Vote Elimination
You could implement a commit-reveal scheme where candidates or third parties submit votes in phases. For example:
Commit Phase: Votes or candidate eliminations are “committed” by hashing the vote data and submitting it on-chain. No one can see the actual vote data yet.
Reveal Phase: After a certain time, the hash is revealed, and the candidate with the fewest votes can be eliminated. By allowing the elimination to happen in phases, you avoid large, gas-heavy computations in a single transaction.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.