Probar contratos inteligentes es realmente importante. ¿Por qué? Los contratos inteligentes son generalmente inmutables en producción, públicos , objetivos de piratas informáticos y, a menudo, implican implicaciones financieras significativas . ¿La historia corta? Con los contratos inteligentes, no querrás equivocarte. Necesita probar mucho, probar a menudo y probar a fondo. Las pruebas de contratos inteligentes involucran a todos los sospechosos habituales (pruebas unitarias, pruebas de integración, pruebas automatizadas), pero también se recomienda encarecidamente el fuzzing, una forma de bombardear su código con un montón de entradas aleatorias solo para ver qué sucede.
Echemos un vistazo a qué es exactamente el fuzzing como método de prueba y cómo puede incluirlo fácilmente en su flujo de trabajo de prueba de software utilizando herramientas como ConsenSys Diligence Fuzzing . Veremos un tutorial usando un contacto inteligente defi de ejemplo para ver cómo se hace.
Fuzzing testing (o fuzzing testing) implica el envío de millones de datos no válidos, inesperados y (semi) aleatorios contra un contrato inteligente en un esfuerzo por provocar un comportamiento inesperado y detectar vulnerabilidades. Es una forma rápida, eficiente y de fuerza bruta de probar casos y escenarios extremos en los que quizás no haya pensado. Complementa sus otros flujos de prueba y sus auditorías.
Fuzzing ha existido durante un tiempo en el desarrollo tradicional de pila completa, pero está aquí una nueva clase de herramientas que puede aplicar fuzzing a las pruebas de contratos inteligentes en web3. Algunas de las herramientas de fuzzing incluyen Echidna y MythX de código abierto.
En este artículo, sin embargo, me sumergiré profundamente en Diligence Fuzzing , que ofrece fuzzing como un servicio, una forma bastante genial y fácil de fuzzing.
Para usar Diligence Fuzzing, anote sus contratos inteligentes usando Scribble . Básicamente, usas anotaciones Scribble en tu código para decirle al fuzzer qué tipo de salida esperar para esa función.
Se ve algo como esto:
/// #invariant {:msg "balances are in sync"} unchecked_sum(_balances) == _totalSupply;
Llama al fuzzer desde una interfaz de usuario web donde envía el código de contrato inteligente. Presiona ejecutar y aproximadamente 10 minutos después, obtiene un informe con los problemas encontrados, la ubicación de los problemas, el porcentaje de cobertura y más.
Esta es una manera fácil y poderosa de verificar algunos casos extremos.
Digamos que tenemos un nuevo protocolo DeFi y el token correspondiente que queremos lanzar. DeFi es finanzas de vanguardia, es divertido escribirlo y es fundamental que no tengamos ningún error. Los contratos inteligentes de DeFi a menudo involucran millones (o más) en fondos de usuarios. Por lo tanto, queremos asegurarnos de que nuestros contratos inteligentes se prueben (y auditen) de la manera más exhaustiva posible.
NOTA IMPORTANTE: ¡Este código tiene vulnerabilidades A PROPÓSITO! Es para que podamos mostrar cómo el fuzzing puede detectar estos errores. Por favor, no uses este código para nada.
Empecemos.
Primero, necesitamos nuestra cuenta Diligence. Regístrese en Diligencia . Obtienes 10 horas de fuzzing gratis para probar cosas.
Haga clic en el nombre de su cuenta en la parte superior derecha, haga clic en "crear nueva clave API" y asígnele un nombre. Esta clave API nos permite conectarnos a la FaaS.
Clona el siguiente repositorio:
https://github.com/ConsenSys/scribble-exercise-1.git
Desde la línea de comandos, navegue hasta la carpeta del repositorio, que ahora será la carpeta raíz de su proyecto. Si es necesario para su máquina, active un Python venv. Luego, ejecute los siguientes comandos para instalar las dependencias de su proyecto:
$ npm i -g eth-scribble ganache truffle $ pip3 install diligence-fuzzing
Edite el archivo .fuzz.yml
para usar la clave API de su cuenta de Diligence para el valor de la clave.
Su archivo resultante debería verse así:
.fuzz.yml fuzz: # Tell the CLI where to find the compiled contracts and compilation artifacts build_directory: build/contracts ... campaign_name_prefix: "ERC20 campaign" # Point to your ganache node which holds the seed rpc_url: "http://localhost:8545" key: "DILIGENCE API KEY GOES HERE" ...
Ahora veamos nuestro contrato inteligente en contracts/vulnerableERC20.sol
. Aquí tenemos un token vulnerable (¡como se llama!) para nuestro nuevo protocolo DeFi. Claramente necesitamos probarlo tanto como sea posible. Entonces, agreguemos una verificación usando Scribble que, con suerte, el fuzzer detectará.
Encima de la definición del contrato, agregue:
/// #invariant "balances are in sync" unchecked_sum(_balances) == _totalSupply;
Y encima de la función de transferencia, agregue:
/// #if_succeeds msg.sender != _to ==> _balances[_to] == old(_balances[_to]) + _value; /// #if_succeeds msg.sender != _to ==> _balances[msg.sender] == old(_balances[msg.sender]) - _value; /// #if_succeeds msg.sender == _to ==> _balances[msg.sender] == old(_balances[_to]); /// #if_succeeds old(_balances[msg.sender]) >= _value;
Así que ahora veamos si el probador de fuzzing detecta algo. Observe las anotaciones que hemos agregado al contrato para ayudar al probador a comprender lo que esperamos que suceda. Y observe en la configuración que el probador está configurado para ejecutarse durante 10 minutos como máximo, por lo que es posible que no detecte todo.
¡Ahora estamos listos para probar! Desde la carpeta raíz del proyecto, ejecute make fuzz
para llamar al archivo make, que compilará, construirá y enviará todo a FaaS.
Vuelva a su tablero y debería ver algo similar a lo que se muestra a continuación. Espere varios segundos para que comience la campaña.
Una vez que termine de generarse, debería ver algo similar a lo que se muestra a continuación:
¡Incluso vemos cobertura de código!
¡Y después de unos minutos, vemos una falla!
Al hacer clic en las propiedades, vemos cualquier infracción. ¡Y sí, tenemos un error! Haga clic en la ubicación de la línea para ver los detalles de nuestras posibles vulnerabilidades de código.
Nos detendremos allí, pero si dejas que el fuzzer siga funcionando, ¡podría encontrar más!
Fuzzing puede ser un paso crucial en su proceso de desarrollo y prueba de software. Le ayuda a identificar posibles vulnerabilidades que, de lo contrario, podrían pasar desapercibidas. Al usarlo, comienza a reconocer y comprender las trampas y los desafíos comunes. ¡Aproveche herramientas como Diligence Fuzzing , escriba mejores contratos inteligentes y conviértase en un mejor desarrollador!