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!
Su código de contrato original no funcionaría correctamente hasta que agregue constant
a 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_call
JSON-RPC funcionara.
geth
instanciaPuede 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 --rpc
parámetro le permitirá usar curl
para comunicarse con su geth
instancia.
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 geth
instancia 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 geth
con 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 geth
instancia sin los parámetros de minería, ya que otra geth
instancia 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 geth
comando anterior:
geth --testnet --rpc console
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 geth
con los parámetros --miner
y ya que su instancia necesitará extraer su cadena de bloques privada:--minerthreads
geth
geth --dev --rpc --mine --minerthreads 1 console
Tuve que modificar su código para que se ejecutara correctamente agregando la palabra constant
a 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.
En su geth
caso, escriba su código aplanado con la asignación a la variable. No se preocupe por el undefined
resultado 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 geth
sesió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 int256
para el a
parámetro en lugar de int
.
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 geth
sesión.
geth
sesiónAntes de agregar constant
a la double
funció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 geth
sesión, DESPUÉS de agregar constant
a la double
función:
> test.double(5)
10
> test.double(55)
110
curl
Y JSON-RPCY 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 data
parámetro.
0x6ffa1caa0000000000000000000000000000000000000000000000000000000000000005
Y usando la dirección del parámetro "¡Contrato extraído!", construimos el siguiente curl
comando:
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...000a
es el valor 10
.
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 params
lista. 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 from
parámetro.
Referencias:
eth_call
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?
data
para 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
.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 int256
argumento de 5
simplemente 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 to
dirección del contrato que se invoca y from
es 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 getData
utilizar como:
var myCallData = myContractInstance.myMethod.getData(param1 [, param2, ...]);
(En este caso, también puede considerar dejar que se invoque web3.js myMethod
en lugar de usar eth_call
JSON-RPC).
data
?getData
en 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.
unlockAccount
está 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)
Guillermo Entriken
Christian Findlay