Skip to content

Latest commit

 

History

History
212 lines (152 loc) · 5.88 KB

File metadata and controls

212 lines (152 loc) · 5.88 KB

Advanced Usage

MockApi addr_validate Patch

During cargo cosmfuzz build, CosmFuzz patches cosmwasm-std's MockApi::addr_validate so it returns Ok(Addr::unchecked(input)). This reduces address-validation noise during fuzzing.

Custom Testing Frameworks

By default, CosmFuzz uses the standard cw-multi-test framework. However, you can customize the generated harness to use alternative testing frameworks.

Why Customize?

  • Use enhanced testing frameworks with more features
  • Integrate with existing test infrastructure
  • Add custom mocking or simulation logic
  • Test against specific chain behaviors

Popular Alternatives

  • sylvia - Framework with macros for contract interfaces and testing
  • cw-orchestrator - Advanced testing and deployment framework
  • mesh-testing - Osmosis's enhanced multi-test framework
  • Custom forks of cw-multi-test with chain-specific features

How to Customize the Harness

After running cargo cosmfuzz build, the generated harness is at:

/tmp/cosmfuzz/{your-project}/src/main.rs

You can edit this file directly! CosmFuzz won't overwrite it on subsequent builds (unless you delete it).

Example Customization

// Instead of:
use cw_multi_test::{App, Executor};

// Use your custom framework:
use my_custom_framework::{CustomApp, CustomExecutor};

// Or use cw-orchestrator:
use cw_orch::{prelude::*, daemon::Daemon};

fn main() {
    ziggy::fuzz!(|data: &[u8]| {
        // Initialize with your custom framework
        let mut app = CustomApp::new_with_custom_features();

        // Your custom setup
        app.enable_custom_module();

        // Rest of fuzzing logic...
        // Parse messages, execute, check invariants
    });
}

Preserving InstantiateMsg Customizations

CosmFuzz automatically preserves InstantiateMsg customizations across rebuilds using regex extraction. When you edit the generated harness to customize initialization:

let cw20_init_msg = cw20_InstantiateMsg {
    name: "Test Token".to_string(),
    symbol: "TEST".to_string(),
    decimals: 6,
    initial_balances: vec![],
    mint: None,
    marketing: None,
};

This customization will be preserved when you run cargo cosmfuzz build again (e.g., after adding new invariants).

Workflow for Custom Harness

  1. Generate initial harness:

    cargo cosmfuzz build
  2. Customize the generated code:

    # Edit the generated harness
    vim /tmp/cosmfuzz/your-project/src/main.rs
    
    # Update dependencies in generated Cargo.toml if needed
    vim /tmp/cosmfuzz/your-project/Cargo.toml
  3. Rebuild and fuzz:

    cd /tmp/cosmfuzz/your-project
    cargo ziggy build
    cargo ziggy fuzz

    Or use CosmFuzz commands:

    cargo cosmfuzz build
    cargo cosmfuzz fuzz
  4. Iterate: Make changes, rebuild, re-fuzz

Chain-Specific Testing

For testing against specific chain features (IBC, custom modules, etc.):

// In your customized harness
use custom_chain_multi_test::ChainApp;

fn main() {
    ziggy::fuzz!(|data: &[u8]| {
        let mut app = ChainApp::new_with_ibc();

        // Set up IBC channels
        let channel = app.create_ibc_channel(...);

        // Fuzz IBC packet handling
        // ... your custom logic
    });
}

Custom Message Parsing

The default harness uses bincode and delimiter-based message parsing. You can customize this:

fn main() {
    ziggy::fuzz!(|data: &[u8]| {
        // Custom parsing logic
        let messages = match parse_custom_format(data) {
            Ok(msgs) => msgs,
            Err(_) => return,  // Invalid input, skip
        };

        // Execute messages...
    });
}

ExecuteMsg Discovery

CosmFuzz attempts to detect the ExecuteMsg import by finding the #[entry_point] execute function and reading its message parameter type. If it cannot resolve the type (or finds multiple candidates), it falls back to importing ExecuteMsg from {contract_crate}::msg::ExecuteMsg and prints a warning during cargo cosmfuzz build.

Adding Custom Invariants

Beyond auto-discovered invariants, you can add custom checks in the harness:

fn main() {
    ziggy::fuzz!(|data: &[u8]| {
        // ... execute messages ...

        // Auto-discovered invariants
        contract::cosmfuzz::invariant_balance_positive(&app, &contract_addr);

        // Custom runtime checks
        assert!(app.block_info().height > 0, "Block height should advance");

        // Custom cross-contract checks
        verify_protocol_solvency(&app, &token_addr, &vault_addr);
    });
}

Tips for Custom Harnesses

  1. Keep invariant calls: Don't remove the auto-generated invariant checks unless you're replacing them
  2. Test your harness: Run it manually with cargo run before fuzzing to ensure it works
  3. Version control: Consider committing your customized harness to your repo
  4. Document changes: Add comments explaining your customizations
  5. Upstream improvements: If you create a useful pattern, consider contributing it back!

Future: Template Mode

Future versions may support custom templates:

cargo cosmfuzz build --template ./my-harness-template.rs

This would allow you to define your own harness structure while keeping invariant discovery automatic.

Example: Custom Harness with Sylvia

use sylvia::cw_multi_test::IntoApp;
use cosmwasm_std::{Addr, coins};

fn main() {
    ziggy::fuzz!(|data: &[u8]| {
        // Create Sylvia app
        let app = sylvia::cw_multi_test::App::default().into_app();

        // Use Sylvia's contract interface
        let contract = MyContract::new(app.clone());

        // Parse and execute messages
        // ... fuzzing logic ...

        // Call invariants
        // ... invariant checks ...
    });
}