¿Cómo obtener valores de retorno cuando se llama a una función sin vista?

Estoy escribiendo a continuación un contrato simple que almacena todos los resultados de los cuestionarios para cada identificación.

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;
  }
}

La función votese puede llamar y extraer con éxito, sin embargo, no puedo obtener el valor de retorno de vote.

Agradecería que alguien aconsejara la causa de esto y la solución para obtener el valor de retorno de la función con argumentos.

Respuestas (3)

Resumen

Su código se ejecuta como se esperaba cuando lo ejecuto a través de la gethconsola. Si no te funciona, prueba a aumentar la gasolina que envías con tus transacciones.

Como @Taylor Gerringha dicho en su respuesta, es posible que no pueda obtener los resultados de su vote()función, pero su código parece funcionar bien.

Si desea el resultado de su vote()función, que en su ejemplo es una verificación de si una persona ha votado antes, ya tiene estos datos en sus votersdatos.



Los detalles

Tomé su código fuente y simplemente cambié el nombre de la clase de answera Answer, y convertí su comentario de //a /*...*/:

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;
  }
}

Eliminé el CR-LF del código, colapsé los espacios y ejecuté la siguiente declaración en 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

Luego compilé su código y lo inserté en mi cadena de bloques de desarrollo:

> 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...

Esperé a que se extrajera el contrato y recibí el siguiente mensaje:

Contract mined! Address: 0xe51ac93e4c28206f0f0296e5b6d66daf0a917bc3
[object Object]

Comprobado getNumResult():

> answer.getNumResult("idOne")
0

Votado:

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

Esperé a que se extrajera la transacción y verifiqué la transacción:

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

Comprobado getNumResult():

> answer.getNumResult("idOne")
1

Envió otro voto con una identificación diferente:

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

Comprueba los resultados:

> answer.getNumResult("idTwo")
1

Enviado otro voto desde la misma cuenta con el mismo ID:

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

Y noté el siguiente error:

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

El error anterior debe haberse generado a partir de la throwdeclaración en su código si se vota la misma ID desde la misma cuenta:

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

Luego envié otro voto desde mi segunda cuenta:

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

Y los resultados se actualizaron como se esperaba:

> answer.getNumResult("idOne")
2
Funcionó para mí, pero tuve que usar requirecon la condición negada por !el operador en lugar de throw.

Actualmente no es posible devolver valores de funciones que modifican la cadena de bloques. Para recibir un valor de retorno, puede marcar las funciones como "pure" o "view" .

Para las funciones de cambio de estado, la única forma de "devolver" información es mediante el uso de Solidity Events, que se fusionan como códigos de operación LOG en la máquina virtual Ethereum.

Me pregunto por qué no puedo obtener un valor de retorno de mi función no constante. ¿ Tengo que usar eventos?
@Eamorr Desde una perspectiva estrictamente técnica, no es imposible, pero las implementaciones actuales de los clientes no se prestan a esa funcionalidad.
Entonces, ¿tiene algún sentido escribir "devoluciones (bool)" en la función "votar ()"?
@Aniket sí, tiene sentido si la función se llama desde otra función de contrato inteligente
¿Cómo conseguimos que esta sea la respuesta aceptada? El anterior es engañoso.

Con la función no constante vote, solo puede recuperar el hash de la transacción inmediatamente porque es posible que la transacción nunca se extraiga. O podría tomar varios bloques como se indica en "Esperando que se extraiga la transacción..." Se recomienda comprobar: ¿ Cuál es la diferencia entre una transacción y una llamada?

Eventos

Se necesitan eventos para obtener el "valor de retorno" de vote.

Ejemplo de cómo agregar y activar un event:

contract answer{
  // ...
  event VoteEvent(string ID, bool returnValue);

  function vote(string ID, uint qNum, uint ans) returns (bool) {
    // ...
    VoteEvent(ID, true);
    return true;
  }
}

Consulte Eventos de contrato para conocer las diferentes formas de observar y obtener datos de eventos mediante web3.js.

Entonces, ¿tiene algún sentido escribir "devoluciones (bool)" en la función "votar ()"?
@AK Si votepudiera return false, por ejemplo, si ya votó, el valor de retorno podría ser útil para que otros contratos puedan hacer cosas como if (!someContract.vote("ak", 7, 2)) {...}. Los valores de retorno pueden ser usados ​​por otros contratos, pero web3.js solo puede usar eventos. Si siempre es return true, entonces probablemente se pueda eliminar el booleano y simplificar el evento a algo como UserVoted(string ID).
lo tengo completamente