Skip to content

Commit 932b3ee

Browse files
committed
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.
1 parent 6fe3410 commit 932b3ee

File tree

5 files changed

+348
-285
lines changed

5 files changed

+348
-285
lines changed

fuzz/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ libfuzzer-sys = "0.4"
1919
bdk_wallet = { path = "../wallet" }
2020

2121
[[bin]]
22-
name = "wallet"
23-
path = "fuzz_targets/wallet_update.rs"
22+
name = "bdk_wallet"
23+
path = "fuzz_targets/bdk_wallet.rs"
2424
test = false
2525
doc = false
2626
bench = false

fuzz/fuzz_targets/bdk_wallet.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#![no_main]
2+
3+
use libfuzzer_sys::fuzz_target;
4+
use std::{
5+
cmp,
6+
collections::{BTreeMap, BTreeSet, HashSet, VecDeque},
7+
};
8+
9+
use bdk_wallet::{
10+
bitcoin::{Network, Txid},
11+
chain::{BlockId, ConfirmationBlockTime, TxUpdate},
12+
descriptor::DescriptorError,
13+
KeychainKind, Update, Wallet,
14+
};
15+
16+
use bdk_wallet::bitcoin::{
17+
absolute::LockTime, transaction::Version, Amount, OutPoint, Transaction, TxIn, TxOut,
18+
};
19+
20+
use bdk_wallet_fuzz::{
21+
fuzz_utils::*, try_consume_anchors, try_consume_bool, try_consume_byte, try_consume_checkpoint,
22+
try_consume_seen_or_evicted_ats, try_consume_txouts, try_consume_txs, try_consume_u32,
23+
try_consume_u64, try_consume_u8,
24+
};
25+
26+
// descriptors
27+
const INTERNAL_DESCRIPTOR: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
28+
const EXTERNAL_DESCRIPTOR: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
29+
30+
// network
31+
const NETWORK: Network = Network::Testnet;
32+
33+
pub enum WalletAction {
34+
ApplyUpdate,
35+
CreateTx,
36+
PersistAndLoad,
37+
}
38+
39+
impl WalletAction {
40+
fn from_byte(byte: &u8) -> Option<WalletAction> {
41+
if *byte == 0x00 {
42+
Some(WalletAction::ApplyUpdate)
43+
} else if *byte == 0x01 {
44+
Some(WalletAction::CreateTx)
45+
} else if *byte == 0x02 {
46+
Some(WalletAction::PersistAndLoad)
47+
} else {
48+
None
49+
}
50+
}
51+
}
52+
53+
fuzz_target!(|data: &[u8]| {
54+
// creates initial wallet.
55+
let wallet: Result<Wallet, DescriptorError> =
56+
Wallet::create(INTERNAL_DESCRIPTOR, EXTERNAL_DESCRIPTOR)
57+
.network(NETWORK)
58+
.create_wallet_no_persist();
59+
60+
// asserts that the wallet creation did not fail.
61+
let mut wallet = match wallet {
62+
Ok(wallet) => wallet,
63+
Err(_) => return,
64+
};
65+
66+
// fuzzed code goes here.
67+
let mut new_data = data;
68+
let mut data_iter = new_data.iter();
69+
while let Some(wallet_action) = WalletAction::from_byte(try_consume_byte!(data_iter)) {
70+
match wallet_action {
71+
WalletAction::ApplyUpdate => {
72+
// generated fuzzed keychain indices.
73+
let mut last_active_indices: BTreeMap<KeychainKind, u32> = BTreeMap::new();
74+
for keychain in [KeychainKind::Internal, KeychainKind::External] {
75+
if try_consume_bool!(data_iter) {
76+
let count = try_consume_u8!(data_iter) as u32;
77+
let start = try_consume_u8!(data_iter) as u32;
78+
last_active_indices.extend((start..count).map(|idx| (keychain, idx)))
79+
}
80+
}
81+
82+
// generate fuzzed tx update.
83+
let txs: Vec<std::sync::Arc<Transaction>> =
84+
try_consume_txs!(&mut new_data, &mut wallet);
85+
86+
let mut unconfirmed_txids: VecDeque<Txid> =
87+
txs.iter().map(|tx| tx.compute_txid()).collect();
88+
89+
let txouts = try_consume_txouts!(&mut new_data);
90+
let anchors = try_consume_anchors!(&mut new_data, unconfirmed_txids);
91+
let seen_ats = try_consume_seen_or_evicted_ats!(&mut new_data, unconfirmed_txids);
92+
let evicted_ats =
93+
try_consume_seen_or_evicted_ats!(&mut new_data, unconfirmed_txids);
94+
95+
// build the tx update with fuzzed data
96+
let mut tx_update = TxUpdate::default();
97+
tx_update.txs = txs;
98+
tx_update.txouts = txouts;
99+
tx_update.anchors = anchors;
100+
tx_update.seen_ats = seen_ats;
101+
tx_update.evicted_ats = evicted_ats;
102+
103+
// generate fuzzed chain.
104+
let chain = try_consume_checkpoint!(&mut new_data, wallet);
105+
106+
// apply fuzzed update.
107+
let update = Update {
108+
last_active_indices,
109+
tx_update,
110+
chain: Some(chain),
111+
};
112+
113+
wallet.apply_update(update).unwrap();
114+
}
115+
WalletAction::CreateTx => {
116+
// todo!()
117+
continue;
118+
}
119+
WalletAction::PersistAndLoad => {
120+
// todo!()
121+
continue;
122+
}
123+
}
124+
}
125+
});

fuzz/fuzz_targets/wallet_update.rs

Lines changed: 0 additions & 74 deletions
This file was deleted.

0 commit comments

Comments
 (0)