Skip to content

Fuzz test

Fuzz Testing Summary

The fuzz testing of the D21 voting system was completed successfully. The script tested various functions, including voter registration, voting, and result sorting. No errors or issues were found, indicating the system's resilience to diverse input scenarios and its adherence to the defined voting rules. This result supports the system's reliability and correctness in its current state.

from wake.testing import *  
from pytypes.contracts.D21 import D21  
from wake.testing.fuzzing import *  
import random  
from typing import Dict, List, Tuple  


class D21FuzzTest(FuzzTest):  
    d21_contract: D21  
    owner: Address  
    subjects: List[Address]  
    tracked_votes: Dict[Address, int]  
    voters: List[Address]  
    votes_cast: Dict[Address, Tuple[int, int]]  # Tuple format: (positive_votes, negative_votes)  
    voted_for_subject: Dict[Address, Dict[Address, bool]]  
    voting_started: bool  # Indicates whether the voting period has started  
    voting_ended: bool # Indicates whether the voting period has ended  

    # Contract initialization and state setup  
    def pre_sequence(self) -> None:  

        self.owner = default_chain.accounts[0]  
        self.d21_contract = D21.deploy(from_=self.owner)  
        self.subjects = []  
        self.voters = []  
        self.votes_cast = {}  
        self.voting_started = False  
        self.voting_ended = False  
        self.tracked_votes = {}  
        self.voted_for_subject = {}  


    @flow(precondition=lambda self: not self.voting_started)  
    def flow_addSubject(self):  
        print("flow_addSubject")  

        subject_name = random_string(5, 20)  
        subject_address = random_address()  
        self.d21_contract.addSubject(subject_name, from_=subject_address)  

        self.subjects.append(subject_address)  
        self.tracked_votes[subject_address] = 0  


    @flow(precondition=lambda self: not self.voting_started)  
    def flow_addVoter(self):  
        print("flow_addVoter")  

        voter = random_account()  
        self.d21_contract.addVoter(voter, from_=self.owner)  
        self.voters.append(voter.address)  
        self.votes_cast[voter.address] = (0, 0)  


    @flow(  
        precondition=lambda self:  
        not self.voting_started and  
        len(self.subjects) > 5 and  
        len(self.voters) > 20  
    )  
    def flow_startVoting(self):  
        print("flow_startVoting")  

        self.d21_contract.startVoting(from_=self.owner)  
        self.voting_started = True  


    @flow(  
        precondition=lambda self:  

        self.voting_started and  
        not self.voting_ended and  
        len(self.subjects) > 0 and  
        len(self.voters) > 0  

    )  
    def flow_votePositive(self):  
        print("flow_votePositive")  

        voter = random.choice(self.voters)  
        subject = random.choice(self.subjects)  
        positive_votes, negative_votes = self.votes_cast.get(voter, (0, 0))  
        vote_status = self.voted_for_subject.get(voter, {}).get(subject, False)  

        if positive_votes < 3 and not vote_status:  
            self.d21_contract.votePositive(subject, from_=voter)  
            self.votes_cast[voter] = (positive_votes + 1, negative_votes)  
            self.tracked_votes[subject] += 1  
            if voter not in self.voted_for_subject:  
                self.voted_for_subject[voter] = {}  
            self.voted_for_subject[voter][subject] = True  
    @flow(  
        precondition=lambda self:  

        self.voting_started and  
        not self.voting_ended and  
        len(self.subjects) > 0 and  
        len(self.voters) > 0  
    )  
    def flow_voteNegative(self):  
        print("flow_voteNegative")  

        voter = random.choice(self.voters)  # Randomly select a voter  
        subject = random.choice(self.subjects)  # Randomly select a subject  

        positive_votes, negative_votes = self.votes_cast.get(voter, (0, 0))  
        vote_status = self.voted_for_subject.get(voter, {}).get(subject, False)  
        # Check if the voter has cast at least two positive votes and no negative votes  
        if positive_votes >= 2 and negative_votes < 1 and not vote_status:  
            self.d21_contract.voteNegative(subject, from_=voter)  
            self.votes_cast[voter] = (positive_votes, negative_votes + 1)  
            self.tracked_votes[subject] -= 1  
            if voter not in self.voted_for_subject:  
                self.voted_for_subject[voter] = {}  
            self.voted_for_subject[voter][subject] = True  

    @invariant()  
    def invariant_checkState(self) -> None:  
        print("invariant_checkState")  

        contract_owner = read_storage_variable(self.d21_contract,'owner')  
        contract_subjects = self.d21_contract.getSubjects()  

        # Check if the contract owner matches the expected owner  
        assert contract_owner == self.owner.address  

        # Check if the list of subjects in the contract matches the expected list  
        assert set(contract_subjects) == set(self.subjects)  


        # Validate the vote counts for each subject  
        for subject_addr in self.subjects:  
            contract_subject = self.d21_contract.getSubject(subject_addr)  
            expected_votes = self.tracked_votes[subject_addr]  
            assert contract_subject.votes == expected_votes  

        # Retrieve the voting results from the contract  
        contract_results = self.d21_contract.getResults()  

        # Check if the results are sorted in descending order by vote count  
        sorted_results = sorted(contract_results, key=lambda subject: subject.votes, reverse=True)  
        assert contract_results == sorted_results  


@default_chain.connect()  
def test_fuzz():  
    D21FuzzTest().run(sequences_count=30, flows_count=100)