I have deployed the contract, the code is here, very easy. Then get the instance ballot1 = eth.contract(abi).at(addr) call the function ballot1.giveRightToVote.sendTransaction(eth.accounts[1], 'xiaoB', {from:eth.accounts[0]}) continue minned after confirmed

ballot1.voters() ["0x0000000000000000000000000000000000000000000000000000000000000000", false, 0, 0, 0]

the state variable was still empty

  • Try ballot1.giveRightToVote.sendTransaction(eth.accounts[1], 'xiaoB', {from:eth.accounts[0], gas:200000}) – The Officious BokkyPooBah Dec 24 '16 at 7:55
  • 1
    @BokkyPooBah I have tried, the transaction has been minned, but the state variable didn't change – user2781581 Dec 24 '16 at 9:18

Try calling ballot1.voters(eth.accounts[1]) instead of just ballot1.voters(). Explanation follows.

When you mark a variable public, solidity creates getter functions, so for example if you make a variable called address public my_address, it will make the equivalent to:

function my_address() constant returns (address) {
   return my_address;

You're trying to use such a getter function for voters. However, voters is a mapping, which is a list of keys (in this case of type address) to values (of type Voter). You might expect that it would have a auto-generated a function so that called without parameters, it would return all the keys to the mapping, or maybe the keys and their values. However, you can't iterate a mapping; There is no way to get the keys in solidity unless you already know them, so the Solidity compiler can't do anything sensible with just a call to a public mapping with no arguments. What it's done instead is to auto-generate a function with an argument for the key. When you're calling it without a parameter, it's imagining you called it with the parameter 0x0000000000000000000000000000000000000000000000000000000000000000, which returns an empty mapping since you haven't set any values for a voter with the address 0x0000000000000000000000000000000000000000000000000000000000000000.

If you do need to iterate through the keys of voters, what you can do is create a parallel data structure in the form of an array called voter_addresses[], make that public, and set it at the same time as you set a value in the voters mapping. If you mark that public, you should (IIRC) be able to get the resulting array by calling voter_addresses().

| improve this answer | |
  • I have modified the code, added a state var votersNum, code like this then executed ballot1 = eth.contract(abi).at(addr); ballot1.giveRightToVote.sendTransaction(eth.accounts[1], 'xiaoB', {from:eth.accounts[0]}); then minned, get the votersNum ballot1.votersNum() it was still 0, didn't increase – user2781581 Dec 24 '16 at 10:50

I modified your code just to change the Solidity version to match my environment:

pragma solidity ^0.4.2;

contract Ballot {
    // 投票人结构体
    struct Voter {
        bytes32 name;
        bool voted;  // 是否已经投过票
        uint vote;   // 投给谁了
        uint givenRightTime; // 被授权时间
        uint votetime; // 投票时间

    // 候选项结构体
    struct Proposal {
        bytes32 name;
        uint voteCount;

    address public chairperson;  // 投票发起人

    mapping(address => Voter) public voters; // 投票群众

    Proposal[] public proposals;  // 候选选项

    event voteCast(address from, bytes32 proposal, uint voteTime);

    // 构造方法
    function Ballot(bytes32[] proposalNames) {
        chairperson = msg.sender;

        // 初始化候选项
        for(uint i = 0; i < proposalNames.length; i++) {
                name: proposalNames[i],
                voteCount: 0


    function giveRightToVote(address voter, bytes32 voterName) {
        // 如果不是投票发起人分配,或者分配的地址已经投过,抛异常
        if(msg.sender != chairperson || voters[voter].voted) {
        voters[voter].name = voterName;
        voters[voter].voted = false;
        voters[voter].votetime = 0;
        voters[voter].givenRightTime = now;

    // 投票
    function vote(uint proposalIndex) {
        Voter sender = voters[msg.sender];
        // 检查是否已经投过
        if(sender.voted) {

        proposals[proposalIndex].voteCount += 1;

        // 修改状态
        sender.voted = true;
        sender.votetime = now;
        sender.vote = proposalIndex;

        voteCast(msg.sender, proposals[proposalIndex].name, now);

    // 获取获胜者编号
    function winningProposalIndex() constant
            returns (uint winningProposalIndex)
        uint winningVoteCount = 0;
        for (uint p = 0; p < proposals.length; p++) {
            if (proposals[p].voteCount > winningVoteCount) {
                winningVoteCount = proposals[p].voteCount;
                winningProposalIndex = p;

    // 获取获胜者姓名
    function winnerName() constant
            returns (bytes32 winnerName)
        winnerName = proposals[winningProposalIndex()].name;

And loaded your code into Browser Solidity, connected to my --dev geth instance with eth.accounts[0] unlocked.

enter image description here

I executed the transaction giveRightToVote(...) with the parameters "0x0014060ff383c9b21c6840a3b14aab06741e5c49","xiaoB" where the address is my eth.accounts[1].

The then called the autogenerated function voters(...) with the parameter "0x0014060ff383c9b21c6840a3b14aab06741e5c49" and it returned the expected results:

enter image description here

In geth, I confirmed the returned value from voters(...) using the command:

> web3.toUtf8("0x7869616f42000000000000000000000000000000000000000000000000000000")

I then ran the following in geth:

> var abi = [{"constant":false,"inputs":[{"name":"proposalIndex","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"name","type":"bytes32"},{"name":"voteCount","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"chairperson","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"winningProposalIndex","outputs":[{"name":"winningProposalIndex","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"voters","outputs":[{"name":"name","type":"bytes32"},{"name":"voted","type":"bool"},{"name":"vote","type":"uint256"},{"name":"givenRightTime","type":"uint256"},{"name":"votetime","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"winnerName","outputs":[{"name":"winnerName","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"voter","type":"address"},{"name":"voterName","type":"bytes32"}],"name":"giveRightToVote","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"proposalNames","type":"bytes32[]"}],"type":"constructor","payable":true},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"proposal","type":"bytes32"},{"indexed":false,"name":"voteTime","type":"uint256"}],"name":"voteCast","type":"event"}];
> var addr = "0x0d41ff746994e76fd0ad575ca852e447559e8089";
> var ballot1 = eth.contract(abi).at(addr);
> ballot1.giveRightToVote.sendTransaction(eth.accounts[2], 'account2user', {from:eth.accounts[0]});
// Wait for the transaction to be mined, then check it worked correctly
> eth.getTransactionReceipt("0x07c24de5b92499f05570a2c648bc8f30ae7a23676bddf29ad00aea4a8b21da58")
  blockHash: "0xf11c183e434266199912cfbb97984c7a527f0c0858dd238dd5bca290cf8c4190",
  blockNumber: 30084,
  contractAddress: null,
  cumulativeGasUsed: 74723,
  from: "0x000d1009bd8f0b1301cc5edc28ed1222a3ce671e",
  gasUsed: 74723,
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  root: "0x6095d7e4c505418b78d12034270b4d88c27d4921c71cbd1767d525cb3c8c5319",
  to: "0x0d41ff746994e76fd0ad575ca852e447559e8089",
  transactionHash: "0x07c24de5b92499f05570a2c648bc8f30ae7a23676bddf29ad00aea4a8b21da58",
  transactionIndex: 0
> ballot1.voters(eth.accounts[2]);
["0x6163636f756e7432757365720000000000000000000000000000000000000000", false, 0, 1482575492, 0]
> web3.toUtf8("0x6163636f756e7432757365720000000000000000000000000000000000000000");

Your code is working as expected. Just follow the steps above. As @Edmund Edgar has stated, you will need to pass the account parameter to ballot1.voters(...).

Update Responding To Comment Below

  1. As you are sending your transaction from from eth.accounts[0], you only have to unlock eth.accounts[0].

  2. You may want to try Browser Solidity - it's a bit easier to interactively work out what is going on. To do this:

    • Point your browser to https://github.com/ethereum/browser-solidity.
    • Change Branch on the top left from master to gh-pages.
    • Download the .zip file onto your computer. The current .zip file has the name browser-solidity-6698a74.zip.
    • Unzip the .zip file into a subdirectory on your computer.
    • Navigate to the new subdirectory.
    • Load index.html in this subdirectory in your web browser.
    • In Browser Solidity, select the cube icon on the top right.
    • Click on Web3 Provider and confirm that you want to connect to your local Ethereum node.
    • Load your Solidity code in the left pane.
    • Click on the Create button after specifying your contract's constructor parameters on the input box to the right of the Create button.
    • Click on the giveRightToVote button after specifying the parameters - see the screenprint I've attached.
    • Click on the voters button after specifying the parameters - see the screenprint I've attached.
| improve this answer | |
  • I have run the steps as you in geth (replace the addr truffle migrate returned), but still not work. ballot1.voters(eth.accounts[2]); returns ["0x0000000000000000000000000000000000000000000000000000000000000000", false, 0, 0, 0] I deployed the contract via truffle, executed truffle compile and truffle migrate, and it returns the address. Except this, all other steps in geth console are exactly the same. – user2781581 Dec 24 '16 at 11:51
  • Could help me with the question above – user2781581 Dec 24 '16 at 12:15
  • I was testing with eth.accounts[2] for a unique address to test with. Presumably you have tested with your appropriate account, eth.accounts[1]? – The Officious BokkyPooBah Dec 24 '16 at 12:21
  • I have 5 accounts,eth.accounts[2] has the value 0x8a0fbdf7aa1d7e8e6aabc51719912f8f6de00330 , I started the geth by this command ./geth --dev --identity "newEth-dev" --rpc --rpccorsdomain "*" --datadir "./cdata-dev" --port 30305 --rpcapi "personal,db,eth,net,web3" --networkid 999 console – user2781581 Dec 25 '16 at 6:49
  • 1
    yes, before I executed truffle migrate, I have unlocked the accounts[0] personal.unlockAccount(eth.accounts[0], '123456', 100000), do you mean that I should unlock acounts[2] ? – user2781581 Dec 25 '16 at 7:02

I have solved the question by adding the parameter gas ballot1.giveRightToVote.sendTransaction(eth.accounts[2], 'account2user', {from:eth.accounts[0], gas: 4700000}); but while I called the function at the second time as above ballot1.giveRightToVote.sendTransaction(eth.accounts[3], 'account3user', {from:eth.accounts[0]}); without gas, it successed too. Really unimaginable

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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