Solidity – Getting Return Values from Non-View Functions

contract-debuggingcontract-invocationsolidityweb3js

I am writing below a simple contract which stores all results of questionnaires for each ID.

contract answer{
  mapping(address => mapping(string => bool)) voters;

  struct qList {
    uint count; //The number of respondents
    mapping(address => mapping(uint => uint)) answer;
  }

  mapping(string => qList) questionnaires;

  function vote(string ID, uint qNum, uint ans) returns (bool) {
    if(voters[msg.sender][ID]) throw;
    voters[msg.sender][ID] = true;
    questionnaires[ID].count += 1;
    questionnaires[ID].answer[msg.sender][qNum] = ans;
    return true;
  }

  function getNumResult(string ID) constant returns (uint res) {
    return questionnaires[ID].count;
  }
}

The function vote can be called and mined successfully, however I cannot get the return value of vote.

Appreciate it if someone would advise the cause of this and solution to get the return value of function with arguments.

Best Answer

Summary

Your code runs as expected when I execute it through the geth console. If it does not work for you, try increase the gas you send with your transactions.

As @Taylor Gerring has stated in his answer, you may not be able to get the results from your vote() function, but your code seems to work OK.

If you want the result from your vote() function, which in your example is a check as to whether a person has voted before, you already have this data in your voters data.



The Details

I've taken your source code and just changed the class name from answer to Answer, and converted your comment from // to /*...*/:

contract Answer {
  mapping(address => mapping(string => bool)) voters;

  struct qList {
    uint count; /* The number of respondents */
    mapping(address => mapping(uint => uint)) answer;
  }

  mapping(string => qList) questionnaires;

  function vote(string ID, uint qNum, uint ans) returns (bool) {
    if (voters[msg.sender][ID]) throw;
    voters[msg.sender][ID] = true;
    questionnaires[ID].count += 1;
    questionnaires[ID].answer[msg.sender][qNum] = ans;
    return true;
  }

  function getNumResult(string ID) constant returns (uint res) {
    return questionnaires[ID].count;
  }
}

I've stripped out the CR-LF from the code and collapsed the spaces, and executed the following statement in geth:

> var answerSource='contract Answer { mapping(address => mapping(string => bool)) voters; struct qList { uint count; /* The number of respondents */ mapping(address => mapping(uint => uint)) answer; } mapping(string => qList) questionnaires; function vote(string ID, uint qNum, uint ans) returns (bool) { if(voters[msg.sender][ID]) throw; voters[msg.sender][ID] = true; questionnaires[ID].count += 1; questionnaires[ID].answer[msg.sender][qNum] = ans; return true; } function getNumResult(string ID) constant returns (uint res) { return questionnaires[ID].count; }}'
undefined

I then compiled your code and inserted it into my dev blockchain:

> var answerCompiled = web3.eth.compile.solidity(answerSource);
undefined

> var answerContract = web3.eth.contract(answerCompiled.Answer.info.abiDefinition);
undefined 

> var answer = answerContract.new({
    from:web3.eth.accounts[0], 
    data: answerCompiled.Answer.code, gas: 2000000}, 
    function(e, contract) {
      if (!e) {
        if(!contract.address) {
          console.log("Contract transaction send: TransactionHash: " 
            + contract.transactionHash + " waiting to be mined...");
        } else {
          console.log("Contract mined! Address: " + contract.address);
          console.log(contract);
        }
    }
})

Contract transaction send: TransactionHash: 0x5b5eb3c6d2a4b43eff4444b71b762911ddc72e239d1d495b6bec7b2e6a738df0 waiting to be mined...

I waited for the contract to be mined and got the following message:

Contract mined! Address: 0xe51ac93e4c28206f0f0296e5b6d66daf0a917bc3
[object Object]

Checked getNumResult():

> answer.getNumResult("idOne")
0

Voted:

> answer.vote("idOne", 1, 1, eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: answerCompiled.Answer.code,
  gas: 1000000
});
"0xfcbf47472733c0922552aa617fc7cb1226c346edc022fbe09ea521dfb75f7699"

Waited for the transaction to be mined and checked the transaction:

> eth.getTransaction("0xfcbf47472733c0922552aa617fc7cb1226c346edc022fbe09ea521dfb75f7699")
{
  ...
  blockNumber: 6567,
  ...
}

Checked getNumResult():

> answer.getNumResult("idOne")
1

Sent another vote with a different ID:

> answer.vote("idTwo", 2, 1, eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: answerCompiled.Answer.code,
  gas: 1000000
});

Checked the results:

> answer.getNumResult("idTwo")
1

Sent another vote from the same account with the same ID:

> answer.vote("idOne", 2, 1, eth.accounts[0], {
  from:web3.eth.accounts[0], 
  data: answerCompiled.Answer.code,
  gas: 1000000
});
"0xbbf9db6eb7c02571948002f56e3a7c56b6c7f55c2a4bbc70a244bb2afbf44e1f"

And I noticed the following error:

PC 00000366: JUMP GAS: 976744 COST: 8 ERROR: invalid jump destination (PUSH1) 2

The error above must have been generated from the throw statement in your code if the same ID is voted from the same account:

if (voters[msg.sender][ID]) throw;

I then sent another vote from my second account:

> answer.vote("idOne", 2, 1, eth.accounts[1], {
  from:web3.eth.accounts[1], 
  data: answerCompiled.Answer.code,
  gas: 1000000
});

And the results were updated as expected:

> answer.getNumResult("idOne")
2
Related Topic