Diseño de contrato por contrato -> corrección y consumo máximo de gas

Esta pregunta es una pregunta de diseño general que se refiere a la corrección del contrato y las funciones que enumero aquí son solo ejemplos.

A medida que me desarrollo en Ethereum, se me hace evidente que podría ser relativamente fácil diseñar mal un contrato que se bloquee solo porque las funciones clave necesarias pueden volverse repentinamente inaccesibles porque su ejecución supera repentinamente el límite de gas.

Eso me lleva a la pregunta probablemente difícil:

¿Cómo puedo afirmar que una función específica nunca usa más de una cierta cantidad de gas? Esto parece ser una necesidad crucial para la corrección del contrato.

Por ejemplo

function unbound() returns (uint256) {
    uint256 total = 0;
    for (uint i=0; i < users.length; i++) {
        total+= users[i].value;
    }
    return total;
}

llamar a esta función tiene un costo de gas no determinista y podría fácilmente en algún momento volverse imposible de llamar. Por supuesto, en un ejemplo tan simple como este, es fácil rediseñar y calcular este total no en un bucle sino por otros medios. Pero no siempre es posible evitar los bucles. La pregunta es, ¿cómo se escriben funciones correctas en ese caso?

Por ejemplo, el sistema podría diseñarse de manera que nunca haya más de 6 usuarios, en cuyo caso el ciclo está vinculado y el costo de ejecución es determinista, por lo que si tengo:

function bound() returns (uint256) {
    uint256 total = 0;
    assert(users.length < 6);
    for (uint i=0; i < users.length; i++) {
        total+= users[i].value;
    }
    return total;
}

Esta función utiliza un bucle pero tiene un límite superior determinista para el consumo de gas.

A: ¿Cómo puedo calcular la cantidad máxima de gas que consumirá esta función?

B: ¿Cómo puedo comunicar este máximo a los clientes de la función?

¿Puedo de alguna manera hacer algo como esto:

function bound() returns (uint256) {
    uint256 total = 0;
    for (uint i=0; i < users.length; i++) {
        total+= users[i].value;
    }
    assert(gasConsumend < 1234);
    return total;
}

?

Estamos escribiendo contratos después de todo, y las condiciones previas y posteriores son una parte integral del diseño por contrato . Podemos escribir condiciones previas con require() y postcondiciones con assert() - pero para la corrección en ethereum también parece necesario afirmar un consumo máximo de gas. ¿Cómo?

Respuestas (1)

Me temo que puede que no haya una respuesta infalible a esto, por la sencilla razón de que en el transcurso de las bifurcaciones, los costos de gasolina pueden cambiar para ciertos códigos de operación (como se hizo para detener los ataques de spam hace algún tiempo).

Debido a lo anterior, es posible que un control de gas codificado de forma rígida ya no sea válido a largo plazo.

Cuando se trata de una función de bucle, requerir que su entrada tenga una cierta longitud es una opción factible. Además, si conoce la entrada, puede usar la eth_estimateGasllamada para realizar una "ejecución en seco" de la transacción y estimar el gas utilizado, que es cómo MyEtherWallet, Metamask, etc. a veces autocompletan el campo de gas máximo.

Tenga en cuenta que la estimación de gas no es perfecta, especialmente para transacciones que cambian según factores externos (como marcas de tiempo o que el hash de bloque sea par/impar).

Desafortunadamente, limitar las longitudes de las matrices a menudo no es factible. Si su matriz debe exceder un tamaño donde no es posible recorrerlo en un tx, una alternativa es reconstruir su función para que acepte una lista de índices como parámetros, y solo acceda a esos índices en esa transacción. Esto puede permitirle actualizar el contenido de la matriz/realizar alguna operación poco a poco en el transcurso de unas pocas transacciones.

Por ejemplo, una variable de suma podría declararse a nivel de contrato, y una matriz de 1 millón de elementos podría sumarse en el transcurso de 5000 transacciones que suman 20 elementos cada una.

Al final, sospecho que la redacción cuidadosa del contrato es la clave para tratar de evitar las situaciones, y un buen diseño de billetera/frontend puede comunicar a los usuarios finales cuándo una acción excederá el límite de gas del bloque y pedirles que reduzcan el tamaño de entrada.

pero eth_estimateGas es una característica de geth, no una característica de EVM a la que se puede acceder de alguna manera mediante contratos, ¿verdad? Parecería que si el costo del gas puede cambiar repentinamente, esto significaría que no hay un solo contrato que pueda garantizar su corrección (en el sentido de que seguirá siendo ejecutable). Esto parece ser un defecto de diseño extremo de una plataforma que se supone que proporciona seguridad y corrección mediante 'contratos'...
Los monederos/clientes front-end deben utilizar la estimación de gas para evitar que los usuarios realicen transacciones flagrantemente incorrectas. Además, si bien los costos de la gasolina pueden cambiar, es extremadamente raro que eso suceda, y una situación en la que salta en una cantidad tan grande que hace que se rompan los contratos normales probablemente se enfrentará rápidamente con una bifurcación de la comunidad. Pero sí, probablemente no haya una manera excelente de garantizar esto a nivel de evm.