Skip to content

Commit abc93c5

Browse files
committed
fuzz: Add test for block and transaction accessors
1 parent ae80912 commit abc93c5

File tree

2 files changed

+166
-0
lines changed

2 files changed

+166
-0
lines changed

fuzz/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,10 @@ path = "fuzz_targets/transaction_roundtrip.rs"
4141
test = false
4242
doc = false
4343
bench = false
44+
45+
[[bin]]
46+
name = "kernel_primitives"
47+
path = "fuzz_targets/kernel_primitives.rs"
48+
test = false
49+
doc = false
50+
bench = false
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#![no_main]
2+
3+
use arbitrary::Arbitrary;
4+
use libfuzzer_sys::fuzz_target;
5+
use bitcoinkernel::{
6+
prelude::*,
7+
Block, Transaction
8+
};
9+
10+
#[derive(Debug, Arbitrary)]
11+
enum FuzzOperation {
12+
ParseBlock {
13+
data: Vec<u8>,
14+
},
15+
ParseTransaction {
16+
data: Vec<u8>,
17+
},
18+
CompareBlockTxExtraction {
19+
block_data: Vec<u8>,
20+
tx_index: u16,
21+
},
22+
}
23+
24+
fuzz_target!(|op: FuzzOperation| {
25+
match op {
26+
FuzzOperation::ParseBlock { data } => {
27+
let Ok(block) = Block::try_from(data.as_slice()) else {
28+
return;
29+
};
30+
31+
let tx_count = block.transaction_count();
32+
assert_eq!(tx_count, block.transactions().count());
33+
34+
for (i, tx) in block.transactions().enumerate().take(1000) {
35+
if let Ok(indexed_tx) = block.transaction(i) {
36+
assert_eq!(tx.txid(), indexed_tx.txid());
37+
}
38+
39+
assert_eq!(tx.inputs().count(), tx.input_count());
40+
assert_eq!(tx.outputs().count(), tx.output_count());
41+
42+
for (j, input) in tx.inputs().enumerate().take(10) {
43+
if let Ok(indexed_input) = tx.input(j) {
44+
let op1 = input.outpoint();
45+
let op2 = indexed_input.outpoint();
46+
assert_eq!(op1.txid(), op2.txid());
47+
assert_eq!(op1.index(), op2.index());
48+
assert_eq!(op1.is_null(), op2.is_null());
49+
}
50+
}
51+
52+
for (j, output) in tx.outputs().enumerate().take(10) {
53+
if let Ok(indexed_output) = tx.output(j) {
54+
assert_eq!(output.value(), indexed_output.value());
55+
assert_eq!(
56+
output.script_pubkey().to_bytes(),
57+
indexed_output.script_pubkey().to_bytes()
58+
);
59+
}
60+
}
61+
}
62+
}
63+
64+
FuzzOperation::ParseTransaction { data } => {
65+
let Ok(tx) = Transaction::try_from(data.as_slice()) else {
66+
return;
67+
};
68+
69+
assert_eq!(tx.inputs().count(), tx.input_count());
70+
assert_eq!(tx.outputs().count(), tx.output_count());
71+
72+
for (i, input) in tx.inputs().enumerate().take(10) {
73+
if let Ok(indexed_input) = tx.input(i) {
74+
let op1 = input.outpoint();
75+
let op2 = indexed_input.outpoint();
76+
assert_eq!(op1.txid(), op2.txid());
77+
assert_eq!(op1.index(), op2.index());
78+
}
79+
}
80+
81+
for (i, output) in tx.outputs().enumerate().take(10) {
82+
if let Ok(indexed_output) = tx.output(i) {
83+
assert_eq!(output.value(), indexed_output.value());
84+
}
85+
}
86+
}
87+
88+
FuzzOperation::CompareBlockTxExtraction {
89+
block_data,
90+
tx_index,
91+
} => {
92+
let Ok(block) = Block::try_from(block_data.as_slice()) else {
93+
return;
94+
};
95+
96+
let Ok(tx_from_block) = block.transaction(tx_index as usize) else {
97+
return;
98+
};
99+
100+
let Ok(tx_bytes) = tx_from_block.consensus_encode() else {
101+
return;
102+
};
103+
104+
let Ok(standalone_tx) = Transaction::try_from(tx_bytes.as_slice()) else {
105+
panic!(
106+
"Transaction at index {} is valid in block but failed to parse standalone (block has {} transactions)",
107+
tx_index,
108+
block.transaction_count()
109+
);
110+
};
111+
112+
assert_eq!(tx_from_block.txid(), standalone_tx.txid());
113+
assert_eq!(tx_from_block.input_count(), standalone_tx.input_count());
114+
assert_eq!(tx_from_block.output_count(), standalone_tx.output_count());
115+
116+
for (i, (orig_input, standalone_input)) in tx_from_block
117+
.inputs()
118+
.zip(standalone_tx.inputs())
119+
.enumerate()
120+
.take(10)
121+
{
122+
let op1 = orig_input.outpoint();
123+
let op2 = standalone_input.outpoint();
124+
assert_eq!(
125+
op1.txid(),
126+
op2.txid(),
127+
"Input {} outpoint txid mismatch",
128+
i
129+
);
130+
assert_eq!(
131+
op1.index(),
132+
op2.index(),
133+
"Input {} outpoint index mismatch",
134+
i
135+
);
136+
}
137+
138+
for (i, (orig_output, standalone_output)) in tx_from_block
139+
.outputs()
140+
.zip(standalone_tx.outputs())
141+
.enumerate()
142+
.take(10)
143+
{
144+
assert_eq!(
145+
orig_output.value(),
146+
standalone_output.value(),
147+
"Output {} value mismatch",
148+
i
149+
);
150+
assert_eq!(
151+
orig_output.script_pubkey().to_bytes(),
152+
standalone_output.script_pubkey().to_bytes(),
153+
"Output {} script mismatch",
154+
i
155+
);
156+
}
157+
}
158+
}
159+
});

0 commit comments

Comments
 (0)