SC-20200125

Smart Contracts Intermediate January 25, 2020

Back to All Tasks

Problem Statement

You are tasked with developing a simple voting smart contract using Solidity. This contract should allow voters to cast their votes for one of three predefined candidates. Each voter is allowed to vote only once, and the voting period is limited to a set number of blocks after deployment. The contract should emit an event every time a vote is cast successfully. Additionally, implement a function that returns the current state of the election results (i.e., how many votes each candidate has received). Ensure that all functions are secure against basic vulnerabilities such as reentrancy and integer overflow/underflow.

Concepts

  • Solidity basics
  • Event emission
  • Transaction handling

Constraints

  • Voters can only vote once
  • The voting period is defined by a specific number of blocks
  • Use events to log each successful vote

Security Notes

  • Prevent double voting using a mapping to track voters
  • Limit the voting duration to prevent eternal voting
  • Be cautious with arithmetic operations to avoid overflow/underflow

Solutions

Javascript Solution

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

contract SimpleVoting {
    // Define an event to log each successful vote
event VoteCasted(address indexed voter, uint256 candidate);

// Enum to represent the state of the election
enum State { Created, Running, Ended }
State public electionState;

// Mapping from voter address to a boolean indicating if they have voted
mapping(address => bool) public hasVoted;

// Array to store vote counts for each candidate (3 candidates in total)
uint256[3] public votesReceived;

// Block number at which the voting starts and ends
uint256 public startBlock;
uint256 public endBlock;

// Constructor to set up the election period
constructor(uint256 _durationInBlocks) {
    require(_durationInBlocks > 0, "Duration must be greater than zero");
    startBlock = block.number;
    endBlock = startBlock + _durationInBlocks;
    electionState = State.Running;
}

// Function for voters to cast their vote
function vote(uint256 candidate) public {
    // Ensure the election is still running
    require(electionState == State.Running, "Election has ended");
    // Ensure the voter has not voted before
    require(!hasVoted[msg.sender], "You have already voted");
    // Ensure the candidate number is valid (0, 1, or 2)
    require(candidate < 3 && candidate >= 0, "Invalid candidate number");
    // Ensure voting period has not expired
    require(block.number <= endBlock, "Voting period has ended");

    // Increment the vote count for the selected candidate
    votesReceived[candidate] += 1;
    // Mark this voter as having voted
    hasVoted[msg.sender] = true;

    // Emit an event to log this successful vote
    emit VoteCasted(msg.sender, candidate);
}

// Function to end the election and calculate results
function endElection() public {
    require(block.number > endBlock, "Election has not ended yet");
    electionState = State.Ended;
}

// Function to get the current state of the election
function currentState() external view returns (string memory) {
    if (electionState == State.Created) return "Created";
    else if (electionState == State.Running && block.number <= endBlock) return "Running";
    else return "Ended";
}
}

This Solidity smart contract implements a simple voting system where voters can cast votes for one of three predefined candidates. The contract ensures that each voter can only vote once by using a mapping to track which addresses have already voted. The voting period is defined by the number of blocks after deployment, and the contract automatically transitions from the Running state to the Ended state once the block number exceeds the endBlock.

The contract includes an event named VoteCasted that logs each successful vote, including the voter's address and the candidate they voted for. This provides a transparent record of all votes cast during the election period.

To prevent common vulnerabilities such as reentrancy attacks, the contract ensures that critical state changes (like marking a voter as having voted) occur before emitting events or calling external functions. Additionally, it uses safe arithmetic operations and checks to ensure that candidate indices are within valid bounds and that no votes are counted after the voting period has ended.

The endElection function allows anyone to finalize the election once the block number exceeds the endBlock, transitioning the contract's state to Ended. The currentState function provides a way to query the current status of the election, returning one of three possible states: Created (before the election starts), Running (during the voting period), or Ended (after the voting period has concluded).