¿Cómo puedo saber cuándo me he quedado sin gasolina programáticamente?

Esto sigue apareciendo en los foros de gitter, así que pensé en hacer y responder esta pregunta: ¿Cómo sabes cuándo te has quedado sin gasolina?

Respuestas (5)

Actualmente no hay una señal clara de que te has quedado sin gasolina. Se agregará un mensaje de registro a una versión futura.

Actualmente, lo que hago es verificar si gasSent == gasUsed. Esto requiere que su código recuerde la cantidad de gas que envió en una transacción en particular y espere a que se extraiga la transacción. Si gasSent == gasUsed, lo más probable es que se haya quedado sin gasolina. En mi código javascript lanzo una excepción. Esto me ha ahorrado varias publicaciones embarazosas en el foro y bastante tiempo.

En teoría, la transacción podría ser minada si toma exactamente la cantidad de gas utilizada. No se me ocurre ninguna forma de averiguarlo en este momento, tal vez alguien podría agregar a esta respuesta. Si está corriendo tan cerca del borde de quedarse sin gasolina, probablemente debería enviar más gasolina de todos modos.

Ofrezco gratis un código que usé para extender la funcionalidad web3 que espera que un nodo local extraiga una transacción. Utiliza promesas, pero adaptarlo al estilo de devolución de llamada no debería ser difícil si no desea usar promesas. Planeo extender esto en el futuro para esperar el consenso de una pluralidad de nodos, pero probablemente no lo compartiré.

No podrá copiar y pegar este código en su código si no ha creado una instancia de registro o no ha creado una función de ayuda para ver si se ha configurado una var. Si aún no los has hecho, deberías...

Debería ser un código mucho más pequeño para escribir esto para llamadas de contrato a contrato, por lo que no mostraré ese ejemplo. El mismo concepto: solo verifique si gasSent == gasUsed.

(Y sí, aficionados de Promise, estoy usando un antipatrón de promesa. Simplemente no he llegado a reescribirlo. La funcionalidad es correcta).

Object.getPrototypeOf(web3.eth).awaitConsensus = function(txhash, gasSent) {
var deferred = Promise.pending();
ethP = this;
filter = this.filter('latest');         // XXX make async
callstack = new Error().stack;
filter.watch(function(error, result) {
    // this callback is called multiple times, so can't promise-then it
    ethP.getTransactionReceiptAsync(txhash).then(function(receipt)  {
    // XXX should probably only wait max 2 events before failing XXX 
    if (receipt && receipt.transactionHash == txhash) {
        filter.stopWatching();
        log.info({txreceipt: receipt});

        if (js.isSet(gasSent)) {
            // note corner case of gasUsed == gasSent.  It could
            // mean used EXACTLY that amount of gas and succeeded.
            // this is a limitation of ethereum.  Hopefully they fix it
            if (receipt.gasUsed >= gasSent) {
                log.error({ badReceipt: receipt });
                log.error({ originalStack: callstack });
                throw(Error("ran out of gas, transaction likely failed!"
                                                                + callstack));
            }
        }

        deferred.resolve(receipt);
    }
    });
});
return deferred.promise.timeout(60000, "awaitConsensus timed out after 60000ms")
        .catch(function(e) {
            log.error(e);
            process.exit(1);
        });

}

Los recibos ahora tienen un statuscampo (actualicé mi respuesta) y es posible que le interese actualizar el código :)
Sí, muchas cosas quedan obsoletas con web3.js v1.0, probablemente gracias a muchos comentarios en este foro. No estoy seguro de qué hacer con esos. Tal vez debería comenzar una meta-discusión en lugar de una para cada pregunta relevante.
Buen punto sobre web3.js v1.0 y publicarlo en meta. Mi comentario aquí fue que puede eliminar el temporizador y simplemente verificar el statuscampo en Bizancio.

Dado que el bloque 4370000 (Byzantium) eth.getTransactionReceipt(transactionHash)devolverá un statuscampo que tiene un valor de 0cuando una transacción ha fallado y 1cuando la transacción ha tenido éxito.

Aquí hay un ejemplo que muestra el statuscampo:

{ blockHash: '0xb1fcff633029ee18ab6482b58ff8b6e95dd7c82a954c852157152a7a6d32785e',
  blockNumber: 4370000,
  contractAddress: null,
  cumulativeGasUsed: 21000,
  gasUsed: 21000,
  logs: [],
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  root: null,
  status: 1, // **** HERE IS THE STATUS FIELD *****
  transactionHash: '0x1421a887a02301ae127bf2cd4c006116053c9dc4a255e69ea403a2d77c346cf5',
  transactionIndex: 0 }

(Los bloques anteriores a 4370000 tendrán un statusvalor nulo).

Más detalles aquí .

Estoy usando web3j Ni el recibo de transacción ni el objeto de transacción tienen estado de campo
@rahul No estoy seguro del estado de las implementaciones de Java, ya que son principalmente esfuerzos de la comunidad; una sugerencia es presentar un problema en su repositorio de Github.
¿Hay otras razones por las que una transacción puede fallar además de quedarse sin gasolina? Si es así, todavía hay algunas verificaciones adicionales para determinar por qué falló la transacción.
@PaulS Los otros errores, por ejemplo, código de operación no válido, no se encontrarán si se usa un compilador decente ethereum.stackexchange.com/questions/2307/… Sí, es posible que un valor statusde 0 signifique algo más que Out of Gas, pero actualmente las personas que interactúan con el ensamblaje EVM hecho a mano son raras. Todavía es útil que señale esto, y una referencia para errores es ethereum.github.io/yellowpaper/paper.pdf con el término de búsqueda "excepcional".

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

Hay 2 casos principales de errores después de que se haya minado una transacción

  • el evm se quedó sin gas: de hecho, la mejor manera de probar es gasUsed==gas
  • el contrato implementado no se pudo implementar: getCode (dirección) == 0x

Para el uso sistemático de estos casos, los envolví en un paquete npm ethereum-web3-plus para quien quiera.

se usa asi:

web3.waitFor(  web3.newInstanceTx("Example"), // your txHash
             function(tx, contract, error) {
                  console.log("callback:", tx, contract, error);
                  // Possible errors : full gas used: <gas used>
                  //                   created contract has no bytecodes
                  if(contract) E = web3.instanceAt("Example", contract);
             } );

Quizás una pregunta ingenua: ¿cuál es la razón por la que esto no se incluye solo como una bandera en el recibo de la transacción? el recibo se genera una vez que se extrae el bloque (por lo tanto, el recibo txn contiene el número de bloque) y también contiene el gas utilizado tan claramente en el momento en que se genera el recibo, la transacción se ha ejecutado... Entonces, ¿por qué el recibo no requiere una confirmación? de si la transacción se completó? Da un poco de miedo que las llamadas de contrato se puedan ejecutar con tan poca visibilidad sobre si el estado se actualizó de acuerdo con el comportamiento esperado del contrato.

Hola. Me temo que deberá hacer esto como una pregunta separada, en lugar de publicar como respuesta a una pregunta existente.
¡Bienvenido a Ethereum! Si tiene otra pregunta, hágala haciendo clic en el botón Preguntar .