Esta pregunta surgió al responder Estado de transacción .
En el siguiente ejemplo, envío gas por 21 000 (la cantidad requerida para una transacción regular). En esta situación gas == gasUsed
, ¿no se puede usar para determinar si el contrato ha generado una excepción?
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1.2345, "ether"), gas: 21000})
"0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3"
> eth.getTransaction("0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3")
{
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
blockNumber: null,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gas: 21000,
gasPrice: 20000000000,
hash: "0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3",
input: "0x",
nonce: 55,
to: "0x4d5bbe7fbc80933ffa90ece988a764e41ee6d018",
transactionIndex: null,
value: 1234500000000000000
}
> eth.getTransactionReceipt("0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3")
{
blockHash: "0xf0af8236ceec7ad1839d67c9934ab062a8d95fa1f88b06139f97dbdfbd1cd842",
blockNumber: 2234,
contractAddress: null,
cumulativeGasUsed: 21000,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gasUsed: 21000,
logs: [],
root: "3280f47a0de1149ad5c5fda421faaf95f303da8a77e83c8ec6ac2b3d8ca27abc",
to: "0x4d5bbe7fbc80933ffa90ece988a764e41ee6d018",
transactionHash: "0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3",
transactionIndex: 0
}
Se puede establecer una transacción con el gas exactamente igual al gas que se usará, usando la función web3.eth.estimateGas para estimar primero el gas requerido.
Entonces, ¿cómo se puede detectar el estado de la transacción de un error lanzado cuando el gas puede ser exactamente el mismo que el gas utilizado para una transacción exitosa?
EDIT 15/06/2016 - Explicando las diferencias de ¿Cómo puedo saber cuándo me he quedado sin gasolina programáticamente?
En esta pregunta, ya SABEMOS cómo detectar programáticamente que nos hemos quedado sin gasolina. Y ya sabemos que este método para detectar una transacción con error no es confiable, ya que gas == gasUsed
es una condición válida para una transacción sin error.
Lo que hace esta pregunta es qué alternativas confiables existen para detectar una transacción de error cuando la situación de "quedarse sin gasolina" no es confiable.
Envié una transacción de prueba (# 1) para averiguar primero cuánto gas usa una transacción de contrato inteligente exitosa. De los resultados de esta transacción, el gas requerido para una transacción exitosa es 26747.
Luego usé esta cantidad de gas de 26747 en mi transacción que iba a ser exitosa (#2). y gasUsed == gas
.
Luego uso esta misma cantidad de gas 26747 en mi transacción que va a ser FRACASADA (#3). y gasUsed == gas
.
La única forma confiable que he encontrado hasta ahora para verificar fácilmente si una transacción de contrato inteligente es exitosa es usar debug.traceTransaction
y verificar el último mensaje de error. Si esto es así, ""
entonces no ocurrió ningún error. Si esto es "Out of gas"
o "invalid jump destination (PUSH1) 2"
, entonces ocurrió un error.
Y aquí hay un pequeño código para determinar el estado de su transacción.
> var status = debug.traceTransaction("0xd23219e2ea10528b245deb9e47993cae2ffd3ffe9fc27aeb808e94fc4e75d37b")
undefined
> if (status.structLogs.length > 0) {
console.log(status.structLogs[status.structLogs.length-1].error)
}
"invalid jump destination (PUSH1) 2"
El valor de retorno anterior será ""
si no hay errores o "Out of gas"
si te quedas sin gasolina.
ACTUALIZACIÓN 29/07/2016: la geth --fast
descarga de blockchain no contiene la información para debug.traceTransaction(...)
mostrar.
Estoy usando el siguiente ejemplo para un contrato inteligente que arroja una excepción si _value <12345:
contract TestStatus {
uint public value;
function setValue(uint256 _value) {
value = _value;
if (_value < 12345) {
throw;
}
}
}
Aplané la fuente a:
var testStatusSource='contract TestStatus { uint public value; function setValue(uint256 _value) { value = _value; if (_value < 12345) { throw; } }}'
Compilé e inserté el contrato en la cadena de bloques:
var testStatusCompiled = web3.eth.compile.solidity(testStatusSource);
var testStatusContract = web3.eth.contract(testStatusCompiled.TestStatus.info.abiDefinition);
var testStatus = testStatusContract.new({
from:web3.eth.accounts[0],
data: testStatusCompiled.TestStatus.code, gas: 1000000},
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: 0x4de11cc54484333036f45a2441563d6badae43d2e2789e0b113b6703a582a879 waiting to be mined...
undefined
...
Contract mined! Address: 0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c
[object Object]
Envié una transacción que no causará un error para determinar el gas requerido:
# Not an error
testStatus.setValue(123456, eth.accounts[0], {
from:web3.eth.accounts[0],
data: testStatusCompiled.TestStatus.code,
gas: 41747
});
...
> eth.getTransactionReceipt("0x727cb3c6846bbb05c8437e97aa32db39a252426b09ed837a76bb364748ce73c4")
{
blockHash: "0x293b4d357d72615c409ba37a6430253d64b4646e0366ac987d1364a258cf81fa",
blockNumber: 2465,
contractAddress: null,
cumulativeGasUsed: 26747,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gasUsed: 26747,
logs: [],
root: "fbb29f9eff2340aa8d7df7feec74671c2b693fb9e7b6ec3b710ca6d64813da0e",
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionHash: "0x727cb3c6846bbb05c8437e97aa32db39a252426b09ed837a76bb364748ce73c4",
transactionIndex: 0
}
El gas requerido es 26747.
Envié una transacción value=123456
que NO fallará y gas==gasUsed
:
testStatus.setValue(123456, eth.accounts[0], {
from:web3.eth.accounts[0],
data: testStatusCompiled.TestStatus.code,
gas: 26747
});
0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1
Estoy usando debug.traceTransaction
para confirmar que esta transacción no ha encontrado un error.
> var status = debug.traceTransaction("0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1")
undefined
> status.structLogs[status.structLogs.length-1].error
""
Y en esta situacióngas(26747) == gasUsed(26747)
eth.getTransaction("0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1")
{
blockHash: "0x961277e2bebfe8332a23d240672b2658eced493b70fd145c99991c3c8651adcc",
blockNumber: 2475,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gas: 26747,
gasPrice: 20000000000,
hash: "0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1",
input: "0x55241077000000000000000000000000000000000000000000000000000000000001e240",
nonce: 66,
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionIndex: 0,
value: 0
}
eth.getTransactionReceipt("0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1")
{
blockHash: "0x961277e2bebfe8332a23d240672b2658eced493b70fd145c99991c3c8651adcc",
blockNumber: 2475,
contractAddress: null,
cumulativeGasUsed: 26747,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gasUsed: 26747,
logs: [],
root: "f850e1e5c99352f47e3409e4e9f966a49c13152e63aaa7bb3765f0b37bfe09e5",
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionHash: "0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1",
transactionIndex: 0
}
Luego envié una transacción value=123
que falló y gas==gasUsed
:
testStatus.setValue(123, eth.accounts[0], {
from:web3.eth.accounts[0],
data: testStatusCompiled.TestStatus.code,
gas: 26747
});
0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a
> var status = debug.traceTransaction("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
undefined
> status.structLogs[status.structLogs.length-1].error
"invalid jump destination (PUSH1) 2"
> eth.getTransaction("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
{
blockHash: "0x22fb5fcaef27dd017efcde8c3f78df7f5168c505210f5d08872a9c2877146044",
blockNumber: 2484,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gas: 26747,
gasPrice: 20000000000,
hash: "0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a",
input: "0x55241077000000000000000000000000000000000000000000000000000000000000007b",
nonce: 67,
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionIndex: 0,
value: 0
}
> eth.getTransactionReceipt("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
{
blockHash: "0x22fb5fcaef27dd017efcde8c3f78df7f5168c505210f5d08872a9c2877146044",
blockNumber: 2484,
contractAddress: null,
cumulativeGasUsed: 26747,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gasUsed: 26747,
logs: [],
root: "ab51ab81c19eb8da7f8d0216d6c2f1d8946e88842b758ff0af13c28ff7e181b4",
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionHash: "0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a",
transactionIndex: 0
}
Entonces, la única forma fácil y confiable que encontré para determinar si una transacción de contrato inteligente tuvo éxito o falló es usar debug.traceTransaction
.
Aquí está el resultado de debug.traceTransaction
la última transacción que falló.
debug.traceTransaction("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
{
gas: 26747,
returnValue: "",
structLogs: [{
depth: 1,
error: "",
gas: 5280,
gasCost: 3,
memory: null,
op: "PUSH1",
pc: 0,
stack: [],
storage: {}
}, {
depth: 1,
error: "",
gas: 5277,
gasCost: 3,
memory: null,
op: "PUSH1",
pc: 2,
stack: ["0000000000000000000000000000000000000000000000000000000000000060"],
storage: {}
}, {
...
}, {
depth: 1,
error: "invalid jump destination (PUSH1) 2",
gas: 129,
gasCost: 8,
memory: ["0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000060"],
op: "JUMP",
pc: 66,
stack: ["0000000000000000000000000000000000000000000000000000000055241077", "0000000000000000000000000000000000000000000000000000000000000022", "000000000000000000000000000000000000000000000000000000000000007b"],
storage: {
0000000000000000000000000000000000000000000000000000000000000000: "000000000000000000000000000000000000000000000000000000000000007b"
}
}]
}
Y aquí hay un breve script para verificar si la transacción pasó o falló:
> var status = debug.traceTransaction("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
undefined
> status.structLogs[status.structLogs.length-1].error
"invalid jump destination (PUSH1) 2"
debug.traceTransaction
.debug.traceTransaction(...)
información de la parte de la cadena de bloques que se sincroniza rápidamente: la sobrecarga es el espacio en disco y el tiempo de sincronización.status.structLogs[status.structLogs.length-1].error
devuelve: "{}"
. ¿Puedo concluir que la transacción pasó? @BokkyPooBahdebug.traceTransaction
pueden haber cambiado. Aquí hay un tx con un error durante la ICO de TokenCard. var status = debug.traceTransaction("0xe686a5fd36ec7921c847b9285c9ea09724bc6065c7374462a448f0d384adad82");
. status.structLogs[status.structLogs.length-1].error
regresa {}
_ pero status.structLogs[status.structLogs.length-1].op
vuelve"Missing opcode 0xfd"
status.structLogs[status.structLogs.length-1].op returns
=> "Missing opcode 0xfd"
¿podría concluir que la transacción se lanzó? Solo quiero tener un identificador para verificar que la transacción se realice. @BokkyPooBahop:JUMP and error: {}
.. ¿significa también lanzamiento de transacción? @BokkyPooBahop:RETURN and error: null
es una transaccion valida? @BokkyPooBahstatus
indicador a los recibos.eth.getTransactionReceipt(transactionHash)
devolverá un status
campo que tiene un valor de 0
cuando una transacción ha fallado y 1
cuando la transacción ha tenido éxito.
El estado del recibo es nulo y, para determinar si no hay gasolina, la transacción debe procesarse a través del EVM, que es lo que hacen algunos exploradores de bloques .
La falta de gas no se puede detectar estáticamente (sin ejecutar la transacción), en el caso general, debido a las implicaciones del problema de detención .
Incluso en el caso de la transacción más simple que tiene y usa exactamente 21 000 gas, la transacción debe ejecutarse porque el destinatario podría rechazar el Ether que se le envía mediante una función de respaldo. Por lo general, el código de una función de respaldo debe ejecutarse para determinar si evita una excepción de falta de combustible.
gas == gasUsed
es una buena heurística práctica para Sin gasolina, dado que es más seguro suministrar una cantidad razonable de gasolina más que web3.eth.estimateGas
porque la gasolina no utilizada se reembolsa.
Aquí está mi código de Python para verificar esto usando Populus y web3.py :
from web3 import Web3
from populus.utils.transactions import wait_for_transaction_receipt
class TransactionConfirmationError(Exception):
"""A transaction was not correctly included in blockchain."""
def confirm_transaction(web3: Web3, txid: str, timeout=60) -> dict:
"""Make sure a transaction was correctly performed.
Confirm that
* The transaction has been mined in blockchain
* The transaction did not throw an error (used up all its gas)
http://ethereum.stackexchange.com/q/6007/620
:raise TransactionConfirmationError: If we did not get it confirmed in time
:return: Transaction receipt
"""
try:
receipt = wait_for_transaction_receipt(web3, txid, timeout)
except Timeout as e:
raise TransactionConfirmationError("Could not confirm tx {} within timeout {}".format(txid, timeout)) from e
tx = web3.eth.getTransaction(txid)
if tx["gas"] == receipt["gasUsed"]:
raise TransactionConfirmationError("Transaction failed (out of gas, thrown): {}".format(txid))
return receipt
debug.traceTransaction(...)
cheque a su código? Entonces obtendrá el error exacto, y también puede detectar condiciones sin error donde gas==gasUsed ?Personalmente, he estado usando una solución diferente al método traceTransaction. Simplemente consiste en tener un registro de eventos (digamos Completed()) llamado al final de las transacciones (función de contrato). El proceso es entonces:
Es más complejo, pero no utiliza traceTransaction, que considero una función de depuración en lugar de un entorno de producción.
ismael
privacidadisahumanright.eth