4

I'm trying to implement the current contract:

contract Mortal {
    address owner;

    event Killed(address indexed from);

    function Mortal() {
        owner = msg.sender;
    }

    function kill() {
        if (msg.sender == owner) {
            Killed(owner);
            suicide(owner);
        }
    }
}

/// @title Voting with delegation.
contract Ballot is Mortal {
    struct Voter {
        uint weight;
        bool voted;
        address delegate;
        uint vote;
    }

    struct Proposal {
        bytes32 name;
        uint voteCount;
    }

    address public chairperson;

    mapping(address => Voter) public voters;

    Proposal[] public proposals;

    event GivenRightToVote(address indexed chairperson, address indexed voter);

    event Created(bytes32[] proposalNames);
    event Voted(address indexed voter, uint proposal);
    event Delegated(address indexed from, address indexed to);

    function Ballot(bytes32[] proposalNames) {
        chairperson = msg.sender;
        voters[chairperson].weight = 1;
        for (uint i = 0; i < proposalNames.length; i++) {
            proposals.push(Proposal({
                name: proposalNames[i],
                voteCount: 0
            }));
        }
        Created(proposalNames);
    }

    function giveRightToVote(address voter) {
        if (msg.sender != chairperson || voters[voter].voted) throw;
        voters[voter].weight = 1;
        GivenRightToVote(chairperson, voter);
    }

    function delegate(address to) {
        Voter sender = voters[msg.sender];
        if (sender.voted) throw;

        while (voters[to].delegate != address(0) &&
               voters[to].delegate != msg.sender)
            to = voters[to].delegate;

        if (to == msg.sender)
            throw;

        sender.voted = true;
        sender.delegate = to;

        Voter delegate = voters[to];
        if (delegate.voted) {
            proposals[delegate.vote].voteCount += sender.weight;
        } else {
            delegate.weight += sender.weight;
        }

        Delegated(msg.sender, to);
    }

    function vote(uint proposal) {
        Voter sender = voters[msg.sender];
        if (!sender || sender.voted) throw;

        sender.voted = true;
        sender.vote = proposal;

        proposals[proposal].voteCount += sender.weight;
        Voted(msg.sender, proposal);
    }

    /// @dev Computes the winning proposal taking all
    /// previous votes into account.
    function winningProposal() constant
        returns (uint winningProposal) {
        uint winningVoteCount = 0;
        for (uint p = 0; p < proposals.length; p++) {
            if (proposals[p].voteCount > winningVoteCount) {
                winningVoteCount = proposals[p].voteCount;
                winningProposal = p;
            }
        }
    }
}

I was able to deploy it and it seems to be working fine in most cases.

I'm watching votes with this callback:

var votedEvt = ballot.Voted({}, '', function(e, result) {
    if (!e) {
        console.log('Voter ' + result.args.voter + ' voted on proposal #' + result.args.proposal);
    } else {
        console.err(e);
    }
})

The problem is when I try to vote with an account that hasn't been given the right to. My watcher is still triggered and shows me:

> Voter 0xbfa3593... voted on proposal #1

The docs say that this line

Voter sender = voters[msg.sender];

should itself throw if there is no msg.sender key on voters mapping, but this is not what I'm getting.

Any thoughts?

0

2 Answers 2

4

In soldidity mappings exist as if all keys were present with their values set to 0x0. For structs this means that all of the fields will be in their 0x0 state.

Since voters is a mapping, you can access any key and it will return the Voter struct that corresponds to that key. Only arrays throw when you access unset indices.

3
  • So, is there any way in which I can verify if an address is not a key for a mapping? Mar 31, 2016 at 12:50
  • All addresses are part of a mapping, but they may virtually map to zero. If you need a list of "voters", you'll need to store them as a flat array too to act as indices into your map. Solidity doesn't support retrieving mapping keys (since 1: keys are hashed and not maintained themselves and 2: if you don't need iteration, it's costs that can be removed). Mar 31, 2016 at 15:59
  • @HenriqueBarcelos you cannot query in a generic way whether a key is present in a mapping. You will either have to check that the returned value is non-zero or use a separate storage mechanism to track the presence of keys. Apr 1, 2016 at 18:36
2

Adding to @Piper's answer, for completeness the code in the docs is:

    Voter sender = voters[msg.sender];
    if (sender.voted)
        throw;

Per @Piper's answer, voters[msg.sender] and sender.voted will never cause an exception, because firstly voters is a mapping and secondly sender is a struct. (And msg always has a sender.)

So in this code, a throw only happens when sender.voted is true.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.