Skip to content

Comments

feat: add initial fuzz testing#271

Draft
oleonardolima wants to merge 15 commits intobitcoindevkit:masterfrom
oleonardolima:feat/add-initial-fuzz-testing
Draft

feat: add initial fuzz testing#271
oleonardolima wants to merge 15 commits intobitcoindevkit:masterfrom
oleonardolima:feat/add-initial-fuzz-testing

Conversation

@oleonardolima
Copy link
Contributor

@oleonardolima oleonardolima commented Jun 24, 2025

Description

It's an initial work towards the goal of #61, it adds a new fuzz crate to the project which is meant to be used to add new fuzz target in order to increase the fuzz coverage.

It's built on top of and with inspiration from previous Antoine's work through the most recent BDK Audit.

So far, this PR:

  • creates a new fuzz crate with cargo fuzz relying on LibFuzzer engine.
  • adds an initial fuzz target, which builds and applies an Update to Wallet.
  • adds both initial fuzzed_data_provider and fuzz_utils with common used fns to build BDK's-specific types from fuzzed bytes.

Still to be done by this PR:

  • write-up the README.md with instructions for other users.
  • add helpful bash scripts, to build the environment, and run the existing fuzz targets.
  • add a new CI step, to briefly run the existing fuzz targets at a certain schedule.
  • add remaining: (i) persist ; (ii) tx creation fuzz targets (by Antoine's existing target).
  • add other fuzz targets.

Notes to the reviewers

Are there any other BDK usage scenarios you'd like to see covered by a fuzz target ? Let's discuss it either on the issue or under this PR comments.

Changelog notice

TBD

Checklists

All Submissions:

@coveralls
Copy link

coveralls commented Jun 24, 2025

Pull Request Test Coverage Report for Build 16808127177

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 84.912%

Totals Coverage Status
Change from base Build 16786013135: 0.0%
Covered Lines: 6669
Relevant Lines: 7854

💛 - Coveralls

@oleonardolima oleonardolima force-pushed the feat/add-initial-fuzz-testing branch 2 times, most recently from 31f422f to 2b885dc Compare June 25, 2025 19:15
@oleonardolima oleonardolima self-assigned this Jun 25, 2025
@oleonardolima oleonardolima added the tests New or improved tests label Jun 25, 2025
@oleonardolima oleonardolima moved this to In Progress in BDK Wallet Jun 25, 2025
@oleonardolima oleonardolima added this to the Wallet 3.0.0 milestone Jun 25, 2025
@evanlinjin
Copy link
Member

Could you include the rationale for having a separate crate for fuzzing? Is it because we need to use nightly cargo?

@TheBlueMatt
Copy link

Rather than requiring use of libFuzzer, it might make sense to build a generic fuzz wrapper, possibly based on the LDK fuzz infra at https://github.com/lightningdevkit/rust-lightning/tree/main/fuzz or the rust-bitcoin fuzz infra. libFuzzer is great and one of the more actively maintained fuzz harnesses, but honggfuzz is in many cases easier to run, so we've found it quite useful to offer both (plus AFL, for those who want that).

@oleonardolima oleonardolima force-pushed the feat/add-initial-fuzz-testing branch 3 times, most recently from 1166295 to 8422bc6 Compare July 24, 2025 00:04
@oleonardolima oleonardolima force-pushed the feat/add-initial-fuzz-testing branch from 8422bc6 to f309e1d Compare July 24, 2025 00:18
@oleonardolima oleonardolima force-pushed the feat/add-initial-fuzz-testing branch from f309e1d to 3b70fa6 Compare July 24, 2025 00:54
@oleonardolima oleonardolima force-pushed the feat/add-initial-fuzz-testing branch from 47ecc5f to 7f1fa74 Compare August 7, 2025 14:58
@sdmg15
Copy link

sdmg15 commented Jan 24, 2026

Hello @oleonardolima thanks for this initial work. I'd like to continue this or work on it with you if you're fine with that. I left you some discord messages too.

I'm adding the link to the report done by Wizardzine in case anyone else wants to read it to help quick reference because the report wasn't that much easy to find.

https://gist.github.com/darosior/4aeb9512d7f1ac7666abc317d6f9453b

@oleonardolima
Copy link
Contributor Author

oleonardolima commented Jan 24, 2026

Hello @oleonardolima thanks for this initial work. I'd like to continue this or work on it with you if you're fine with that. I left you some discord messages too.

I'm adding the link to the report done by Wizardzine in case anyone else wants to read it to help quick reference because the report wasn't that much easy to find.

https://gist.github.com/darosior/4aeb9512d7f1ac7666abc317d6f9453b

Thanks, any help with adding other fuzz targets and enhancements are appreciated, though I need to do some cleanup on this PR first, but feel free to send your suggestions on top of this one on the fork.

- creates a new `fuzz` crate, it's meant to run fuzz testing over
  bdk_wallet targets, with `cargo fuzz` (libFuzzer).
- creates an initial `wallet_update` fuzz target for `bdk_wallet`.
- creates an initial `fuzzed_data_provider` and `fuzz_utils` files with
  useful methods to consume the fuzzed data into `bdk_wallet` API-specific types.

test(fuzzing): improve `bdk_wallet` fuzz target

- renames the fuzz target to `bdk_wallet`.
- add the `WalletAction` enum, in order to fuzz test different
  behaviors: wallet update, persistance/load, and tx creation.
- use macros (e.g `try_consume_*`) in `fuzzed_data_provider` and
  `fuzz_utils` in order to properly handle an exhausted fuzzer byte
stream, returning early.
- update `Wallet::ApplyUpdate` target to use the newly added macros.
- update `bdk_wallet_fuzz` to use `rusqlite` feature.
- update the created wallet in `bdk_wallet` fuzz target to use an
  in-memory sqlite database connection, initializing wallet with
persistance.
- add the `PersistAndLoad` scenario to `bdk_wallet` fuzz target.
- add the `CreateTx` scenario to `bdk_wallet` fuzz target.
- add two new macros: `try_consume_tx_builder` and
  `try_consume_sign_options`, in order to build the specific structures
and types required for tx creation, signing and applying to wallet.
@oleonardolima oleonardolima force-pushed the feat/add-initial-fuzz-testing branch from 7f1fa74 to a6a44d3 Compare February 12, 2026 18:24
@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.67%. Comparing base (f84cc23) to head (a6a44d3).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #271   +/-   ##
=======================================
  Coverage   86.67%   86.67%           
=======================================
  Files          25       25           
  Lines        8487     8487           
=======================================
  Hits         7356     7356           
  Misses       1131     1131           
Flag Coverage Δ
rust 86.67% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

oleonardolima and others added 4 commits February 12, 2026 15:49
- Move wallet code to wallet/ subdirectory
- Create workspace root Cargo.toml with wallet and fuzz members
- Update fuzz crate to use path dependency for bdk_wallet
- Maintain all existing functionality while improving project organization

This restructure enables better isolation of the fuzz testing infrastructure
and makes the project more modular.

Co-Authored-By: Claude <noreply@anthropic.com>
- Add arbitrary crate dependency with derive feature
- Create arbitrary_types module with basic wrapper types:
  - FuzzedTxid for transaction IDs
  - FuzzedBlockHash for block hashes
  - FuzzedOutPoint for transaction output references
  - FuzzedAmount with reasonable constraints (weighted towards smaller values)
  - FuzzedScript with size limits
- Implement conversion methods to BDK wallet types

This is the foundation for migrating from macro-based fuzzing to
structure-aware fuzzing using the Arbitrary trait.

Co-Authored-By: Claude <noreply@anthropic.com>
- Add FuzzedWalletAction enum with ApplyUpdate, CreateTx, and PersistAndLoad
- Implement FuzzedSignOptions with all signing configuration fields
- Add FuzzedTapLeavesOptions for taproot signing control
- Create FuzzedTxBuilderOptions with transaction builder parameters:
  - Fee configuration (rate vs absolute)
  - Transaction options (locktime, version, ordering)
  - UTXO selection options
  - Output configuration options
- Use weighted probabilities for more realistic option generation

These types provide structured inputs for wallet operations instead of
raw byte manipulation.

Co-Authored-By: Claude <noreply@anthropic.com>
- Add FuzzedTxInput and FuzzedTxOutput with conversion methods
- Implement FuzzedTransaction with wallet-aware output generation
- Create FuzzedAnchor for confirmation data
- Add FuzzedTxUpdate with all transaction update components
- Implement FuzzedCheckpoint for chain progression
- Create FuzzedUpdate combining all wallet update elements
- Include conversion methods that integrate with wallet state
- Use reasonable limits and weighted probabilities for data generation

These types enable structured generation of blockchain data for fuzzing
instead of manual byte manipulation.

Co-Authored-By: Claude <noreply@anthropic.com>
oleonardolima and others added 7 commits February 12, 2026 16:12
- Add FuzzedRecipient for transaction outputs
- Create FuzzedTxBuilder with comprehensive configuration:
  - Recipients list with amounts and scripts
  - Fee configuration (rate or absolute)
  - UTXO selection (manual add/unspendable marking)
  - Transaction options (locktime, version, ordering, sighash)
  - Change spending policies
  - PSBT options (witness UTXOs, xpubs, scripts)
  - Drain wallet and dust handling
- Implement build_with_wallet() method that:
  - Supports both normal transactions and fee bumps
  - Integrates with wallet addresses for better coverage
  - Handles all builder configuration options
  - Pre-collects data to avoid borrow conflicts

This provides a complete abstraction for transaction building during fuzzing.

Co-Authored-By: Claude <noreply@anthropic.com>
- Add FuzzedWalletOperation enum combining all wallet operations:
  - ApplyUpdate with full update data
  - CreateTransaction with builder, sign options, and finalization
  - PersistAndLoad for state verification
- Create FuzzInput struct as the main entry point with operation sequence
- Implement execute() method that:
  - Processes all operations sequentially
  - Handles PersistedWallet instead of raw Wallet
  - Signs and finalizes transactions when configured
  - Verifies wallet state consistency
  - Gracefully handles expected errors
- Export descriptor and network constants for reuse
- Use weighted probabilities for operation selection

This provides a complete execution engine for structured fuzzing inputs.

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace macro-based byte parsing with structured FuzzInput
- Use the arbitrary trait for automatic input generation
- Simplify target to just create wallet and execute operations
- Remove all manual data parsing and macro invocations
- Let the FuzzInput::execute() method handle all operation logic

The fuzz target is now clean, simple, and type-safe, delegating all
complexity to the structured arbitrary types.

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove fuzzed_data_provider.rs with all macro definitions
- Remove fuzz_utils.rs with macro-based helper functions
- Update lib.rs to only export the new arbitrary_types module
- Add comprehensive documentation to lib.rs
- Update README with detailed information about:
  - Architecture and benefits of structure-aware fuzzing
  - How to run and add new fuzz targets
  - How to reproduce crashes
  - Key components and their purpose

This completes the migration from macro-based fuzzing to structure-aware
fuzzing using the Arbitrary trait, resulting in cleaner, more maintainable,
and more effective fuzz testing.

Co-Authored-By: Claude <noreply@anthropic.com>
Optimizations implemented:
- Limit operations to 1-5 per run (was unlimited)
- Reduce transaction sizes to 1-3 inputs, 1-2 outputs (was 10 each)
- Use simple fixed scripts or wallet addresses (was complex arbitrary)
- Simplify updates with flags instead of full data structures
- Remove expensive PersistAndLoad operation
- Streamline transaction builder to essential options only

Performance improvements:
- ~15-20% increase in executions per second (142-150 vs 125)
- Lower memory usage
- Faster coverage growth
- Better bug-finding efficiency

The optimized fuzzer demonstrates that domain-specific optimizations
can significantly improve fuzzing effectiveness while maintaining
comprehensive testing coverage.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tests New or improved tests

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

6 participants