Cómo llamar a un método de contrato usando la API eth_call JSON-RPC

Ok, estoy tratando de llamar a un método de contrato utilizando los métodos proporcionados por la interfaz Ethereum JSON RPC. El JSON RPC se ejecuta en una máquina Ubuntu. Desafortunadamente, no puedo obtener un resultado del contrato de prueba que creé.

Lo primero que hice fue iniciar Go Ethereum en Testnet con:

geth --rpc --testnet

Luego abro otra consola y abro la consola JS con

geth attach

Luego compilé mi contrato usando Browser Solidity, donde mi código es:

 contract test { function double(int a) returns(int) {   return 2*a;   } }

Lo que me da el siguiente código de bytes:

606060405260728060106000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480636ffa1caa146037576035565b005b604b60048080359060200190919050506061565b6040518082815260200191505060405180910390f35b6000816002029050606d565b91905056

Luego creé el contrato con:

curl localhost:8545 -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from": "0x8aff0a12f3e8d55cc718d36f84e002c335df2f4a", "data": "606060405260728060106000396000f360606040526000357c0100000000000000000000000000000000000000000000000000000000900480636ffa1caa146037576035565b005b604b60048080359060200190919050506061565b6040518082815260200191505060405180910390f35b6000816002029050606d565b91905056"}],"id":1}

Lo que me devuelve el hash de la transacción, que uso para obtener la dirección del contrato:

curl localhost:8545 -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x8290c22bd9b4d61bc57222698799edd7bbc8df5214be44e239a95f679249c59c"],"id":1}'

Lo que me devuelve lo siguiente:

{"id":1,"jsonrpc":"2.0","result":{"transactionHash":"0x8290c22bd9b4d61bc57222698799edd7bbc8df5214be44e239a95f679249c59c","transactionIndex":"0x0","blockNumber":"0xd32da","blockHash":"0xf13f185f0eb1e4797885400e3b371c972eedebcf3eef27815a45b649283ec669","cumulativeGasUsed":"0x14293","gasUsed":"0x14293","contractAddress":"0x5c7687810ce3eae6cda44d0e6c896245cd4f97c6","logs":[]}}

Luego quise llamar al método "doble" con el número "5". La documentación del contrato ABI dice que debe tomar los primeros 4 bytes del hash de Keccak.
La firma del método es

double(int)

Lo que da el hash con web3.sha3("doble(int)":

6740d36c7d42fba12b8eb3b047193c9761224c267d7a2e96dc50949860652317

Los primeros cuatro bytes, con el prefijo "0x" son:

0x6740d36c

Luego, la documentación le dice que tome el parámetro, lo codifique en hexadecimal y lo rellene a 32 bytes. Que sería lo siguiente, usando el número "5":

0x0000000000000000000000000000000000000000000000000000000000000005

En total, la cadena codificada debería verse así:

0x6740d36c0000000000000000000000000000000000000000000000000000000000000005

Entonces, usando esto para llamar al método:

 curl localhost:8545 -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from": "0x8aff0a12f3e8d55cc718d36f84e002c335df2f4a", "to": "0x5c7687810ce3eae6cda44d0e6c896245cd4f97c6", "data": "0x6740d36c0000000000000000000000000000000000000000000000000000000000000005"}, "latest"],"id":1}'

Lo que me da este resultado:

{"id":1,"jsonrpc":"2.0","result":"0x"}

¿Estoy haciendo algo mal aquí u olvidando un paso importante?

Además, ¿hay alguna forma de acceder a los métodos ya existentes dentro de la biblioteca Go Ethereum que me liberaría de tener que implementar, por ejemplo, métodos para el relleno yo mismo?

¡Muchas gracias de antemano!

¿Alguien implementó este contrato en la red principal para que podamos seguirlo?
Estoy usando JSON RPC a través de C#. Entonces, realmente necesito ver el equivalente en Json. ¿Hay algún lugar donde pueda ver esto pero con Json?

Respuestas (3)

Resumen

  • Su código de contrato original no funcionaría correctamente hasta que agregue constanta la definición de la función para indicar que esta función no modifica la cadena de bloques.

  • Tuve que usar la firma del método de double(int256)en lugar de double(int)para que eth_callJSON-RPC funcionara.



Los detalles

Ejecute su gethinstancia

Puede usar la cadena de bloques Testnet que está sincronizada con otros pares a través de Internet, o puede usar una cadena de bloques de desarrollo privada que solo está disponible en su computadora y es más rápida para desarrollar. El --rpcparámetro le permitirá usar curlpara comunicarse con su gethinstancia.

Cadena de bloques de la red de prueba

Deberá crear una cuenta y extraer algunas monedas, ya que necesita las monedas para insertar su código en la cadena de bloques o para enviar transacciones a la cadena de bloques:

geth --testnet account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: 
Repeat Passphrase: 
Address: {aaaa725f2f36a28c01bc47f883534990c9c8bbbb}

Su gethinstancia tendrá que sincronizar su cadena de bloques Testnet con otros pares en Internet. Esto puede tomar una hora o más. Una vez sincronizado, comenzará su operación de minería y debería extraer algunas monedas en 10 o 20 minutos. Comience gethcon los parámetros de minería:

geth --testnet --rpc --mine --minerthreads 1 console

Para verificar si tiene monedas, ejecute el comando:

> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether");
25

Si desea salir de geth, presione Control-D.

Si lo desea, ahora puede ejecutar su gethinstancia sin los parámetros de minería, ya que otra gethinstancia en algún lugar de Internet extraerá sus transacciones. Use el siguiente comando para eliminar la opción de minería, o simplemente use el gethcomando anterior:

geth --testnet --rpc console

Cadena de bloques de desarrollo

Deberá crear una cuenta y extraer algunas monedas:

geth --dev account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: 
Repeat Passphrase: 
Address: {cccc725f2f36a28c01bc47f883534990c9c8dddd}

Comience gethcon los parámetros --minery ya que su instancia necesitará extraer su cadena de bloques privada:--minerthreadsgeth

geth --dev --rpc --mine --minerthreads 1 console


Aplane su código y asígnelo a una variable

Tuve que modificar su código para que se ejecutara correctamente agregando la palabra constanta la definición de su función, ya que esta función no modifica la cadena de bloques cuando se ejecuta.

En su ejemplo, su código normalmente estará formateado para facilitar la lectura:

contract Test { 
    function double(int a) constant returns(int) {
        return 2*a;
    } 
}

Puede usar un servicio como la Herramienta de eliminación de saltos de línea para eliminar los saltos de línea, o consulte Cómo cargar el archivo fuente de Solidity en geth para conocer algunas alternativas.

Su código aplanado se verá como el ejemplo que publicó:

contract Test { function double(int a) constant returns(int) { return 2*a; } }

Deberá asignar su código a una variable de cadena:

var testSource='contract Test {  function double(int a) constant returns(int) { return 2*a; } }'

Tenga en cuenta que //los comentarios causarán algunos problemas cuando su código si se aplana, ya que todo el texto después del //comentario se tratará como un comentario. Úselo /* ... */en su lugar si necesita agregar comentarios a su código.


Compile su código

En su gethcaso, escriba su código aplanado con la asignación a la variable. No se preocupe por el undefinedresultado ya que esta es una respuesta normal de geth:

> var testSource='contract Test {  function double(int a) constant returns(int) { return 2*a; } }'
undefined

Compila tu código:

> var testCompiled = web3.eth.compile.solidity(testSource);
I0503 09:04:15.907715    3190 solidity.go:114] solc, the solidity compiler commandline interface
Version: 0.3.2-0/Release-Linux/g++/Interpreter

path: /usr/bin/solc
undefined

Si desea ver la forma binaria de su código:

testCompiled
{
  Test: {
    code: "0x6060604052602a8060106000396000f3606060405260e060020a60003504636ffa1caa8114601a575b005b6002600435026060908152602090f3",
    info: {
      abiDefinition: [{...}],
      compilerOptions: "--bin --abi --userdoc --devdoc --add-std --optimize -o /tmp/solc497335011",
      compilerVersion: "0.3.2",
      developerDoc: {
        methods: {}
      },
      language: "Solidity",
      languageVersion: "0.3.2",
      source: "contract Test {  function double(int a) constant returns(int) { return 2*a; } }",
      userDoc: {
        methods: {}
      }
    }
  }
}

Necesitará la firma de la interfaz binaria de la aplicación (ABI) para su contrato si desea acceder a ejecutar su función fuera de esta gethsesión:

> testCompiled.Test.info.abiDefinition
[{
    constant: true,
    inputs: [{
        name: "a",
        type: "int256"
    }],
    name: "double",
    outputs: [{
        name: "",
        type: "int256"
    }],
    type: "function"
}]

Nota : Esta es la segunda pista de por qué su llamada JSON RPC no funcionó: la firma del método tiene int256para el aparámetro en lugar de int.


Inserte su código en la cadena de bloques

Ejecute los siguientes comandos para insertar su código en la cadena de bloques:

> var testContract = web3.eth.contract(testCompiled.Test.info.abiDefinition);
undefined
> var test = testContract.new({
    from:web3.eth.accounts[0], 
    data: testCompiled.Test.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);
        }
    }
})
I0503 09:09:39.632499    3190 xeth.go:1026] Tx(0x8c15e8b8bb593d4a680b084a455ba82b103e60204638a3674ef1fb90ca0ad4f0) created: 0x7a2d8fa11aa28097135eb514f6a23ba12501191e
Contract transaction send: TransactionHash: 0x8c15e8b8bb593d4a680b084a455ba82b103e60204638a3674ef1fb90ca0ad4f0 waiting to be mined...
undefined

Espere unos minutos y verá el siguiente resultado:

I0503 09:10:34.319772    3190 worker.go:569] commit new work on block 11122 with 0 txs & 0 uncles. Took 431.081µs
Contract mined! Address: 0x7a2d8fa11aa28097135eb514f6a23ba12501191e
[object Object]

Debe guardar la dirección si desea ejecutar este contrato desde fuera de su gethsesión.


Use su contrato dentro de la misma gethsesión

Antes de agregar constanta la doublefunción, estaba obteniendo los siguientes resultados:

> test.double(5)
invalid address

Nota : Esta es la primera pista de por qué falló su llamada JSON RPC.

Para usar su contrato dentro de su gethsesión, DESPUÉS de agregar constanta la doublefunción:

> test.double(5)
10
> test.double(55)
110


Use su contrato usando curlY JSON-RPC

Y ahora llegamos a la parte complicada.

Su función double(int)es realmente double(int256). De Solidez - Tipos :

int• / uint•: Enteros con y sin signo de varios tamaños. Palabras clave uint8 a uint256 en pasos de 8 (sin signo de 8 hasta 256 bits) e int8 a int256. uint e int son alias para uint256 e int256, respectivamente.

En geth, ejecutamos el siguiente comando para encontrar la firma de la double(...)función:

> web3.sha3('double(int256)')
"6ffa1caacdbca40c71e3787a33872771f2864c218eaf6f1b2f862d9323ba1640"

Y tome los primeros 4 bytes para crear el dataparámetro.

0x6ffa1caa0000000000000000000000000000000000000000000000000000000000000005

Y usando la dirección del parámetro "¡Contrato extraído!", construimos el siguiente curlcomando:

curl localhost:8545 -X POST --data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"from": "eth.accounts[0]", "to": "0x65da172d668fbaeb1f60e206204c2327400665fd", "data": "0x6ffa1caa0000000000000000000000000000000000000000000000000000000000000005"}], "id":1}'

Y ejecute este comando desde la línea de comandos:

user@Kumquat:~$ curl localhost:8545 -X POST --data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"from": "eth.accounts[0]", "to": "0x65da172d668fbaeb1f60e206204c2327400665fd", "data": "0x6ffa1caa0000000000000000000000000000000000000000000000000000000000000005"}], "id":1}'
{"id":1,"jsonrpc":"2.0","result":"0x000000000000000000000000000000000000000000000000000000000000000a"}

El resultado de 0x0000...000aes el valor 10.


Actualización 18 de octubre de 2016

Como se documenta en el error de RPC "valor no válido o faltante para los parámetros" al llamar a las funciones de anulación constante del contrato , es posible que deba agregar un parámetro de bloque a la paramslista. Su comando con el parámetro de bloque será:

user@Kumquat:~$ curl localhost:8545 -X POST --data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"from": "eth.accounts[0]", "to": "0x65da172d668fbaeb1f60e206204c2327400665fd", "data": "0x6ffa1caa0000000000000000000000000000000000000000000000000000000000000005"}, "latest"], "id":1}'
{"id":1,"jsonrpc":"2.0","result":"0x000000000000000000000000000000000000000000000000000000000000000a"}

Es posible que también deba omitir el fromparámetro.

Referencias:


Tu otra pregunta

Además, ¿hay alguna forma de acceder a los métodos ya existentes dentro de la biblioteca Go Ethereum que me liberaría de tener que implementar, por ejemplo, métodos para el relleno yo mismo?

¿Podría preguntar esto en otra pregunta ya que esta respuesta ya es demasiado larga?

La parte difícil fue proporcionar los datos. Estuve atascado en obtener el formulario datapara mi función durante todo el día, incluso después de una explicación tan excelente. Pero do we really need to do all this for providing data? Más tarde descubrí getData(..)que hizo todo esto por mí y no necesité ninguno de los pasos complicados. Solo var callData=myContract.myFunction.getData(parameters)y obtuve el data.
Además, solo para su información, el nombre de la función es 'doble', por lo que no es un tipo dentro de un tipo.

Para usar eth_call, la parte complicada es proporcionar el data, que es la codificación ABI de la función que desea invocar y sus argumentos.

Es esencial usar tipos canónicos , por lo que double(int256)en lugar de double(int).

> web3.sha3('double(int256)').substr(0, 8)
"6ffa1caa"

Los tipos de relleno y dinámicos deben especificarse correctamente. En este ejemplo, el int256argumento de 5simplemente se rellena:

0000000000000000000000000000000000000000000000000000000000000005

Concatenando lo anterior da:

0x6ffa1caa00000000000000000000000000000000000000000000000000000000000000005

que efectivamente significa double(5), y especifíquelo como data:

curl localhost:8545 -X POST --data '{"jsonrpc":"2.0", "método":"eth_call", "params":[{"from": "0xsenderAccount", "to": "0xcontractAddress", "datos": "0x6ffa1caa00000000000000000000000000000000000000000000000000000000000000005"}], "id":1}'

es la todirección del contrato que se invoca y fromes la cuenta del remitente (normalmente eth.accounts[0]).


EDITAR: Más información sobre "la parte difícil es proporcionar el data":

web3.js se puede configurar y getDatautilizar como:

var myCallData = myContractInstance.myMethod.getData(param1 [, param2, ...]);

(En este caso, también puede considerar dejar que se invoque web3.js myMethoden lugar de usar eth_callJSON-RPC).

¿Qué pasa si la función exceptúa más de un parámetro, como una función que acepta una dirección y un uint, cómo se obtiene data?
@PrashantPrabhakarSingh Algunas referencias ethereum.stackexchange.com/questions/7675/… lo que lleva a ethereum.stackexchange.com/questions/3364/… EDITAR: vi que encontró web3.js getDataen su comentario que funciona (pero dado que eso implica configurar web3 .js, parece más fácil dejar que web3.js invoque la función también en lugar de RPC eth_call).

Considere desbloquear la cuenta antes de enviar la transacción:

personal.unlockAccount(eth.accounts[0], 'your password goes here', 9000)

o llamada RPC:

curl your_ethereum_node_address:port -X POST --data '{"jsonrpc":"2.0","method":"personal_unlockAccount","params": ["your account address","password of your account",null],"id":1}'

Estuve luchando bastante tiempo para averiguar por qué el contrato no estaba en la cola.

Consulte el elemento 3 en ¿Cómo reducir las posibilidades de que su Ethereum Wallet sea pirateada? . Al menos en las versiones anteriores de geth, su contraseña se almacena en su archivo de texto de historial.
¿Cómo puedo desbloquear la cuenta usando la clave privada en lugar de la contraseña de la cuenta y hacer la transacción @michail?
unlockAccountestá en desuso y no funciona en 2019, en su lugar, use private_key y haga la firma y luego envíe_raw_transaction (o un método similar)