Skip to content

Commit 7c296aa

Browse files
authored
perf(decode): improve score_signature heuristic (#402)
* perf(decode): improve `score_signature` heuristic * fix(tests): make tests pass
1 parent 432fa6c commit 7c296aa

File tree

3 files changed

+40
-17
lines changed

3 files changed

+40
-17
lines changed

crates/common/src/ether/signatures.rs

+24-7
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ impl ResolveSelector for ResolvedFunction {
305305
}
306306
}
307307

308-
pub fn score_signature(signature: &str) -> u32 {
308+
pub fn score_signature(signature: &str, num_words: Option<usize>) -> u32 {
309309
// the score starts at 1000
310310
let mut score = 1000;
311311

@@ -314,7 +314,27 @@ pub fn score_signature(signature: &str) -> u32 {
314314
score -= signature.len() as u32;
315315

316316
// prioritize signatures with less numbers
317-
score -= (signature.matches(|c: char| c.is_numeric()).count() as u32) * 3;
317+
score -= (signature.split("(").next().unwrap_or("").matches(|c: char| c.is_numeric()).count()
318+
as u32) *
319+
3;
320+
321+
// prioritize signatures with parameters
322+
let num_params = signature.matches(',').count() + 1;
323+
score += num_params as u32 * 10;
324+
325+
// count the number of parameters in the signature, if enabled
326+
if let Some(num_words) = num_words {
327+
let num_dyn_params = signature.matches("bytes").count() +
328+
signature.matches("string").count() +
329+
signature.matches("[").count();
330+
let num_static_params = num_params - num_dyn_params;
331+
332+
// reduce the score if the signature has less static parameters than there are words in the
333+
// calldata
334+
if num_static_params < num_words {
335+
score -= (num_words - num_static_params) as u32 * 10;
336+
}
337+
}
318338

319339
score
320340
}
@@ -481,10 +501,7 @@ mod tests {
481501
#[test]
482502
fn score_signature_should_return_correct_score() {
483503
let signature = String::from("test_signature");
484-
let score = score_signature(&signature);
485-
let expected_score = 1000 -
486-
(signature.len() as u32) -
487-
(signature.matches(|c: char| c.is_numeric()).count() as u32) * 3;
488-
assert_eq!(score, expected_score);
504+
let score = score_signature(&signature, None);
505+
assert_eq!(score, 996);
489506
}
490507
}

crates/decode/src/core/mod.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -134,14 +134,20 @@ pub async fn decode(mut args: DecodeArgs) -> Result<DecodeResult, Error> {
134134

135135
if matches.len() > 1 {
136136
debug!("multiple possible matches found. as of 0.8.0, heimdall uses a heuristic to select the best match.");
137+
let num_words = calldata[4..].chunks(32).len();
138+
137139
matches.sort_by(|a, b| {
138-
let a_score = score_signature(&a.signature);
139-
let b_score = score_signature(&b.signature);
140+
let a_score = score_signature(&a.signature, Some(num_words));
141+
let b_score = score_signature(&b.signature, Some(num_words));
140142
b_score.cmp(&a_score)
141143
});
142144
// debug print
143145
for match_ in &matches {
144-
debug!(" > {}: {}", match_.signature, score_signature(&match_.signature));
146+
debug!(
147+
" > {}: {}",
148+
match_.signature,
149+
score_signature(&match_.signature, Some(num_words))
150+
);
145151
}
146152
} else if matches.is_empty() {
147153
warn!("couldn't find any resolved matches for '{}'", function_selector);
@@ -153,7 +159,7 @@ pub async fn decode(mut args: DecodeArgs) -> Result<DecodeResult, Error> {
153159
// chunk in blocks of 32 bytes
154160
let calldata_words = calldata[4..].chunks(32).map(|x| x.to_owned()).collect::<Vec<_>>();
155161

156-
// while calldata_words is not empty, iterate over it
162+
// while calldata_words is not empty, iterate over itcar
157163
let mut i = 0;
158164
let mut covered_words = HashSet::new();
159165
while covered_words.len() != calldata_words.len() {

crates/decompile/src/core/mod.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ pub async fn decompile(args: DecompilerArgs) -> Result<DecompileResult, Error> {
185185
// sort by score, take the highest
186186
let mut potential_values = v.clone();
187187
potential_values.sort_by(|a: &ResolvedError, b: &ResolvedError| {
188-
let a_score = score_signature(&a.signature);
189-
let b_score = score_signature(&b.signature);
188+
let a_score = score_signature(&a.signature, None);
189+
let b_score = score_signature(&b.signature, None);
190190
b_score.cmp(&a_score)
191191
});
192192

@@ -217,8 +217,8 @@ pub async fn decompile(args: DecompilerArgs) -> Result<DecompileResult, Error> {
217217
// sort by score, take the highest
218218
let mut potential_values = v.clone();
219219
potential_values.sort_by(|a: &ResolvedLog, b: &ResolvedLog| {
220-
let a_score = score_signature(&a.signature);
221-
let b_score = score_signature(&b.signature);
220+
let a_score = score_signature(&a.signature, None);
221+
let b_score = score_signature(&b.signature, None);
222222
b_score.cmp(&a_score)
223223
});
224224

@@ -246,8 +246,8 @@ pub async fn decompile(args: DecompilerArgs) -> Result<DecompileResult, Error> {
246246
);
247247

248248
matched_resolved_functions.sort_by(|a, b| {
249-
let a_score = score_signature(&a.signature);
250-
let b_score = score_signature(&b.signature);
249+
let a_score = score_signature(&a.signature, None);
250+
let b_score = score_signature(&b.signature, None);
251251
b_score.cmp(&a_score)
252252
});
253253

0 commit comments

Comments
 (0)