Estaba mirando los documentos y estoy buscando una aclaración sobre la diferencia entre require
y assert
y throw
y revert
.
afirmar (condición bool): cancelar la ejecución y revertir los cambios de estado si la condición es falsa (uso para error interno)
require (condición booleana): cancela la ejecución y revierte los cambios de estado si la condición es falsa (uso para entrada mal formada)
Específicamente con respecto a assert
y require
, ¿cómo traza la línea entre una entrada mal formada y un error interno?
Hay dos aspectos a considerar al elegir entre assert()
yrequire()
assert(false)
compila a 0xfe
, que es un código de operación no válido, consume todo el gas restante y revierte todos los cambios.
require(false)
compila 0xfd
cuál es el REVERT
código de operación, lo que significa que reembolsará el gas restante. El código de operación también puede devolver un valor (útil para la depuración), pero no creo que sea compatible con Solidity en este momento. (2017-11-21)
De los documentos (énfasis mío)
La función require debe usarse para garantizar que se cumplan condiciones válidas, como entradas o variables de estado del contrato, o para validar valores de retorno de llamadas a contratos externos. Si se usan correctamente, las herramientas de análisis pueden evaluar su contrato para identificar las condiciones y llamadas de función que llegarán a una aserción fallida. El código que funciona correctamente nunca debe llegar a una declaración de afirmación fallida; si esto sucede, hay un error en su contrato que debe corregir.
El extracto anterior es una referencia al todavía (a partir del 2017-11-21) experimental e indocumentado SMTChecker
.
Utilizo algunas heurísticas para ayudarme a decidir cuál usar.
require()
para:require(external.send(amount))
owned
situación de contratorequire
más a menudo,assert()
para:assert
menos a menudoBásicamente, assert
solo está ahí para evitar que suceda algo realmente malo, pero no debería ser posible que la condición se evalúe como falsa.
Las funciones require()
y assert()
se agregaron a Solidity antes de la bifurcación Byzantium, en v0.4.10
. Antes de Byzantium, se comportaban de manera idéntica, pero ya compilados en diferentes códigos de operación. Esto significó que algunos contratos implementados antes de Byzantium se comportaron de manera diferente después de la bifurcación, siendo la principal diferencia que comenzaron a reembolsar el gas no utilizado.
assert()
y require()
el cambio de reversión se escriben en la cadena de bloques para que balance[_to] =balance[_from] +_value
se reviertan si assert()
se require()
activa una condición, ¿no es así? (Estoy hablando después del hardfork del año pasado). ¿O se assert()
sigue haciendo el cambio?solc
: gist.github.com/maurelian/02904ae729fb11213cde20ba05a202e6 Le advertirá que es posible que la segunda afirmación sea verdadera para ciertos valores.Estoy usando require
para la validación de entrada, ya que es un poco más eficiente que if/throw.
function foo(uint amount) {
require(amount < totalAmount);
...
}
Donde as assert
debería usarse más para la captura de errores en tiempo de ejecución:
function foo(uint amount) {
...
__check = myAmount;
myAmount -= amount;
assert(myAmount < __check);
...
}
revert
revertirá los cambios y reembolsará el gas no utilizado en una versión posterior de Ethereum, pero ATM actúa de la misma manera que throw.
assert
argumento. Hacerlo podría conducir a una pesadilla de depuración en la que las operaciones se encadenaban para el estado pero se olvidaban para la aserción.assert()
y require()
el cambio de reversión se escriben en la cadena de bloques para que balance[_to] =balance[_from] +_value
se reviertan si assert()
se require()
activa una condición, ¿no es así? (Estoy hablando después del hardfork del año pasado). ¿O se assert()
sigue haciendo el cambio? Pero ambos assert()
y require()
el cambio de reversión se escriben en la cadena de bloques para que balance[_to] =balance[_from] +_value
se reviertan si assert()
se require()
activa una condición, ¿no es así? (Estoy hablando después del hardfork del año pasado). ¿O se assert()
sigue haciendo el cambio?Creo que ninguna de las respuestas es correcta.
assert
está reservado para condiciones con las que se espera que las herramientas de análisis de código estático (tal vez el compilador de Solidity en versiones futuras) puedan detectar el error que advierte al desarrollador en el momento de la compilación.
require
está reservado para condiciones de error de datos de entrada incorrectos a funciones (en comparación con datos de entrada esperados/válidos) que no se pueden detectar hasta el momento de la ejecución. Esto corresponde a las condiciones previas de la función en el lenguaje de programación argot. El compilador no puede ayudar debido a las infinitas posibilidades de los datos de entrada.
throw
está en desuso a favor de revertir.
revert
está reservado para condiciones de error que afectan la lógica empresarial. Por ejemplo, alguien envía un voto cuando la votación ya está cerrada.
require
y revert
son en su mayoría similares con respecto a la implementación interna de EVM, pero los desarrolladores apreciarán la distinción.
Assert
es adecuado para verificar condiciones que no deberían ocurrir pero ocurren.
Require
es adecuado para verificar las condiciones no deseadas que pueden ocurrir.
Solidity tiene un SMTChecker que hace que su uso sea assert
muy bueno porque puede probar que sus invariantes son verdaderas:
Solidity implementa un enfoque de verificación formal basado en SMT (teorías del módulo de satisfacción) y resolución de Horn . El módulo SMTChecker intenta probar automáticamente que el código cumple con las especificaciones dadas por
require
yassert
declaraciones. Es decir, considerarequire
las declaraciones como suposiciones e intenta demostrar que las condiciones dentro de lasassert
declaraciones son siempre verdaderas. Si se encuentra una falla en la aserción, se le puede dar un contraejemplo al usuario que muestre cómo se puede violar la aserción. Si el SMTChecker no da ninguna advertencia para una propiedad, significa que la propiedad es segura.
assert
para ayudarlo a encontrar invariantes que han sido violados.Usas assert
para ayudarte a atrapar cuando sucede lo imposible.
El tutorial de SMTChecker proporciona un buen ejemplo de assert
que es demasiado largo para incluirlo aquí: recomiendo leerlo.
Si el SMTChecker lo emociona para comenzar a usar assert
, eso es algo muy bueno. Pero recuerde que son para invariantes , como mencionan los documentos de Solidity :
Assert solo debe usarse para probar errores internos y verificar invariantes. El código que funciona correctamente nunca debe crear un pánico, ni siquiera en una entrada externa no válida. Si esto sucede, entonces hay un error en su contrato que debe corregir. Las herramientas de análisis de lenguaje pueden evaluar su contrato para identificar las condiciones y llamadas de función que causarán un pánico.
Pablo Razvan Berg