testutils only.Expand description
Support for fuzzing Soroban contracts with cargo-fuzz.
This module provides a pattern for generating Soroban contract types for the
purpose of fuzzing Soroban contracts. It is focused on implementing the
Arbitrary trait that cargo-fuzz relies on to feed fuzzers with
generated Rust values.
This module
- defines the
SorobanArbitrarytrait, - reexports the
arbitrarycrate and theArbitrarytype.
This module is only available when the “testutils” Cargo feature is defined.
§About cargo-fuzz and Arbitrary
In its basic operation cargo-fuzz fuzz generates raw bytes and feeds them
to a program-dependent fuzzer designed to exercise a program, in our case a
Soroban contract.
cargo-fuzz programs declare their entry points with a macro:
fuzz_target!(|input: &[u8]| {
// feed bytes to the program
});More sophisticated fuzzers accept not bytes but Rust types:
#[derive(Arbitrary, Debug)]
struct FuzzDeposit {
deposit_amount: i128,
}
fuzz_target!(|input: FuzzDeposit| {
// fuzz the program based on the input
});Types accepted as input to fuzz_target must implement the Arbitrary trait,
which transforms bytes to Rust types.
§The SorobanArbitrary trait
Soroban types are managed by the host environment, and so must be created
from an Env value; Arbitrary values though must be created from
nothing but bytes. The SorobanArbitrary trait, implemented for all
Soroban contract types, exists to bridge this gap: it defines a prototype
pattern whereby the fuzz_target macro creates prototype values that the
fuzz program can convert to contract values with the standard soroban
conversion traits, FromVal or IntoVal.
The types of prototypes are identified by the associated type,
SorobanArbitrary::Prototype:
pub trait SorobanArbitrary:
TryFromVal<Env, Self::Prototype> + IntoVal<Env, Val> + TryFromVal<Env, Val>
{
type Prototype: for <'a> Arbitrary<'a>;
}Types that implement SorobanArbitrary include:
i32,u32,i64,u64,i128,u128,I256,U256,(), andbool,Error,Bytes,BytesN,Vec,Map,Address,Symbol,Val,
All user-defined contract types, those with the contracttype attribute,
automatically derive SorobanArbitrary. Note that SorobanArbitrary is
only derived when the “testutils” Cargo feature is active. This implies
that, in general, to make a Soroban contract fuzzable, the contract crate
must define a “testutils” Cargo feature, that feature should turn on the
“soroban-sdk/testutils” feature, and the fuzz test, which is its own crate,
must turn that feature on.
§Example: take a Soroban Vec of Address as fuzzer input
use soroban_sdk::{Address, Env, Vec};
use soroban_sdk::testutils::arbitrary::SorobanArbitrary;
fuzz_target!(|input: <Vec<Address> as SorobanArbitrary>::Prototype| {
let env = Env::default();
let addresses: Vec<Address> = input.into_val(&env);
// fuzz the program based on the input
});§Example: take a custom contract type as fuzzer input
use soroban_sdk::{Address, Env, Vec};
use soroban_sdk::contracttype;
use soroban_sdk::testutils::arbitrary::{Arbitrary, SorobanArbitrary};
use std::vec::Vec as RustVec;
#[derive(Arbitrary, Debug)]
struct TestInput {
deposit_amount: i128,
claim_address: <Address as SorobanArbitrary>::Prototype,
time_bound: <TimeBound as SorobanArbitrary>::Prototype,
}
#[contracttype]
pub struct TimeBound {
pub kind: TimeBoundKind,
pub timestamp: u64,
}
#[contracttype]
pub enum TimeBoundKind {
Before,
After,
}
fuzz_target!(|input: TestInput| {
let env = Env::default();
let claim_address: Address = input.claim_address.into_val(&env);
let time_bound: TimeBound = input.time_bound.into_val(&env);
// fuzz the program based on the input
});Re-exports§
pub use arbitrary;
Traits§
- Generate arbitrary structured values from raw, unstructured data.
- An
Env-hosted contract value that can be randomly generated.
Functions§
- fuzz_
catch_ panic Deprecated Catch panics within a fuzz test.