Fuzzing
Fuzzing es el proceso de proporcionar datos aleatorios a los programas para identificar comportamientos inesperados, como bloqueos y pánicos.
Las pruebas de fuzz también se pueden escribir como pruebas de propiedades que, en lugar de buscar pánicos y bloqueos, afirman que alguna propiedad permanece verdadera. El fuzzing, como se demuestra aquí y en otros lugares de estos documentos, utilizará principios de tanto la prueba de propiedades como el fuzzing, pero solo usará el término fuzzing para referirse a ambos.
Los siguientes pasos se pueden utilizar en cualquier espacio de trabajo de contrato Stellar. Si estás experimentando, pruébalos en el ejemplo de incremento. El contrato tiene una función increment
que aumenta el valor de un contador en uno en cada invocación.
Cómo Escribir Pruebas de Fuzz
-
Instala
cargo-fuzz
.cargo install --locked cargo-fuzz
-
Inicializa un proyecto de fuzz ejecutando el siguiente comando dentro de tu directorio de contrato.
cargo fuzz init
-
Abre el archivo
Cargo.toml
del contrato. Agregalib
como uncrate-type
.[lib]
-crate-type = ["cdylib"]
+crate-type = ["lib", "cdylib"] -
Abre el archivo generado
fuzz/Cargo.toml
. Agrega la dependenciasoroban-sdk
.[dependencies]
libfuzzer-sys = "0.4"
+soroban-sdk = { version = "*", features = ["testutils"] } -
Abre el archivo generado
fuzz/src/fuzz_target_1.rs
. Se verá como el de abajo.#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
// fuzzed code goes here
}); -
Completa la llamada
fuzz_target!
con la configuración de prueba y las afirmaciones. Por ejemplo, para el ejemplo de incremento:#![no_main]
use libfuzzer_sys::fuzz_target;
use soroban_increment_with_fuzz_contract::{IncrementContract, IncrementContractClient};
use soroban_sdk::{
testutils::arbitrary::{self, Arbitrary},
Env,
};
#[derive(Debug, Arbitrary)]
pub struct Input {
pub by: u64,
}
fuzz_target!(|input: Input| {
let env = Env::default();
let id = env.register(IncrementContract, ());
let client = IncrementContractClient::new(&env, &id);
let mut last: Option<u32> = None;
for _ in input.by.. {
match client.try_increment() {
Ok(Ok(current)) => assert!(Some(current) > last),
Err(Ok(_)) => {} // Expected error
Ok(Err(_)) => panic!("success with wrong type returned"),
Err(Err(_)) => panic!("unrecognised error"),
}
}
}); -
Ejecuta el objetivo de fuzz.
cargo +nightly fuzz run --sanitizer=thread fuzz_target_1
informaciónSi estás desarrollando en macOS, necesitas agregar el flag
--sanitizer=thread
para solucionar una incidencia conocida.
Esta prueba utiliza los mismos patrones usados en pruebas unitarias y pruebas de integración:
- Crea un entorno, el
Env
. - Registra el contrato que se va a probar.
- Invoca funciones usando un cliente.
- Afirmar expectativas.
Para un ejemplo completo y detallado, consulta el ejemplo de fuzzing.
Hay otra herramienta para fuzzing de código Rust, cargo-afl
. Consulta el [Rust Fuzz book] para un tutorial sobre cómo usarlo.
Guías en esta categoría:
📄️ Pruebas Unitarias
Las pruebas unitarias son pruebas pequeñas que prueban contratos inteligentes.
📄️ Simulación
Simular contratos de dependencia en pruebas.
📄️ Pruebas de Integración
Las pruebas de integración utilizan contratos de dependencia en lugar de simulaciones.
📄️ Fuzzing
Fuzzing y pruebas de propiedades para encontrar comportamientos inesperados.
📄️ Pruebas Diferenciales
Las pruebas diferenciales detectan cambios no intencionados.
📄️ Pruebas Diferenciales con Imágenes de Prueba
Prueba diferencial utilizando imágenes de prueba automáticas.
📄️ Pruebas de Mutación
La prueba de mutación encuentra código no probado.
📄️ Prueba de Autorización
Escribe pruebas que verifiquen la autorización del contrato.
📄️ Eventos de Prueba
Escribe pruebas que verifiquen los eventos de contrato.