From 243e266241a5ef29ee22551875a6f160e7fd2c72 Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Fri, 16 May 2025 12:22:31 +0200 Subject: [PATCH 1/5] changed vector to map --- lib/bytecode_verification/parse_json.rs | 60 +++++++++++++------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/lib/bytecode_verification/parse_json.rs b/lib/bytecode_verification/parse_json.rs index 0da4060..344b519 100644 --- a/lib/bytecode_verification/parse_json.rs +++ b/lib/bytecode_verification/parse_json.rs @@ -326,15 +326,17 @@ impl ProjectInfo { .unwrap() .to_string(); if identifier.starts_with("t_struct") { - let struct_slots: Vec<(u64, U256, Option)> = vec![( - type_name - .get("referencedDeclaration") - .unwrap() - .as_u64() - .unwrap(), + let struct_slots: HashMap)> = HashMap::from([( U256::from_str("0x0").unwrap(), // this won't be used as we only have to add the types - None, - )]; + ( + type_name + .get("referencedDeclaration") + .unwrap() + .as_u64() + .unwrap(), + None, + ), + )]); let mut storage: Vec = vec![]; // this won't be used as we only have to add the types for source in sources.values() { if let Some(ast) = source.ast.clone() { @@ -440,14 +442,14 @@ impl ProjectInfo { sources: &BTreeMap, node: &EAstNode, type_defs: &Types, - struct_slots: &Vec<(u64, U256, Option)>, + struct_slots: &HashMap)>, types: &mut HashMap, storage: &mut Vec, ) { if node.node_type == NodeType::StructDefinition && node.id.is_some() { let mut storage_var_id: Option = None; // parse all struct definitions for each struct -> slot pair. - for (struct_id, slot, name) in struct_slots { + for (slot, (struct_id,name)) in struct_slots { let struct_id = *struct_id; let node_id = node.id.unwrap() as u64; if node_id == struct_id { @@ -593,7 +595,7 @@ impl ProjectInfo { types: &mut HashMap, ) { // Tuple: (struct AST ID, slot, name of variable containing the slot) - let mut struct_slots: Vec<(u64, U256, Option)> = vec![]; + let mut struct_slots: HashMap)> = HashMap::new(); // find pairs (storage slot => struct AST ID) for source in sources.values() { if let Some(ast) = source.ast.clone() { @@ -626,7 +628,7 @@ impl ProjectInfo { sources: &BTreeMap, node: &EAstNode, exported_ids: &Vec, - struct_slots: &mut Vec<(u64, U256, Option)>, + struct_slots: &mut HashMap)>, ) { if node.node_type == NodeType::ContractDefinition && node.id.is_some() @@ -700,7 +702,7 @@ impl ProjectInfo { top_node, stmt_ref["declaration"].as_u64().unwrap() ) { - struct_slots.push((struct_id, var_slot, Some(var_name))); + struct_slots.insert(var_slot, (struct_id, Some(var_name))); // if no variable declaration can be found, try to find // functions with the variable as parameter. } else if let Some((_, _, function_id, param_id)) @@ -754,8 +756,8 @@ impl ProjectInfo { top_node, var_ref_id.as_u64().unwrap() ) { - if !struct_slots.iter().any(|(_, slot, _)| slot.eq(&var_slot)) { - struct_slots.push((struct_id, var_slot, Some(var_name))); + if !struct_slots.contains_key(&var_slot) { + struct_slots.insert(var_slot, (struct_id, Some(var_name))); } } } @@ -764,11 +766,11 @@ impl ProjectInfo { // as we have no associated variable for the slot, // we use the name of the outer function. let var_slot = U256::from_str(slot_value.as_str().unwrap()).unwrap(); - if !struct_slots.iter().any(|(_, slot, _)| slot.eq(&var_slot)) { - struct_slots.push( + if !struct_slots.contains_key(&var_slot) { + struct_slots.insert( + var_slot, ( struct_id, - var_slot, Some(format!("[{}]", outer_function)) ) ); @@ -810,16 +812,18 @@ impl ProjectInfo { } else { function_name = None; } - struct_slots.push(( - struct_id, - U256::from_str( - slot_value - .as_str() - .unwrap(), - ) - .unwrap(), - function_name, - )); + let var_slot = U256::from_str( + slot_value + .as_str() + .unwrap(), + ) + .unwrap(); + if !struct_slots.contains_key(&var_slot) { + struct_slots.insert( + var_slot, + (struct_id, function_name) + ); + } } } } From 1937be92231a17d70cb2d40cc915440386b53e26 Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Fri, 16 May 2025 13:56:37 +0200 Subject: [PATCH 2/5] added another uniqueness check --- src/dvf.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dvf.rs b/src/dvf.rs index 28ec65e..9d3eb8a 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -1004,7 +1004,12 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { ); contract_state.add_forge_inspect(&implementation_layout); - storage.extend(tmp_project_info.storage.clone()); + // Extend storage with implementation storage variables, ensuring unique slots + for storage_var in &tmp_project_info.storage { + if !storage.iter().any(|existing| existing.slot == storage_var.slot) { + storage.push(storage_var.clone()); + } + } types.extend(tmp_project_info.types.clone()); imp_project_info = Some(tmp_project_info); } From ce00cdbc902f7fac92f0896bb1d19b98dc0032a6 Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Fri, 16 May 2025 15:06:53 +0200 Subject: [PATCH 3/5] fmt + clippy --- lib/bytecode_verification/parse_json.rs | 28 ++++++++----------------- src/dvf.rs | 5 ++++- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/lib/bytecode_verification/parse_json.rs b/lib/bytecode_verification/parse_json.rs index 344b519..c5054ff 100644 --- a/lib/bytecode_verification/parse_json.rs +++ b/lib/bytecode_verification/parse_json.rs @@ -449,7 +449,7 @@ impl ProjectInfo { if node.node_type == NodeType::StructDefinition && node.id.is_some() { let mut storage_var_id: Option = None; // parse all struct definitions for each struct -> slot pair. - for (slot, (struct_id,name)) in struct_slots { + for (slot, (struct_id, name)) in struct_slots { let struct_id = *struct_id; let node_id = node.id.unwrap() as u64; if node_id == struct_id { @@ -756,9 +756,7 @@ impl ProjectInfo { top_node, var_ref_id.as_u64().unwrap() ) { - if !struct_slots.contains_key(&var_slot) { - struct_slots.insert(var_slot, (struct_id, Some(var_name))); - } + struct_slots.entry(var_slot).or_insert((struct_id, Some(var_name))); } } } else if let Some(slot_value) = arg[param_id].get("value") { @@ -766,15 +764,7 @@ impl ProjectInfo { // as we have no associated variable for the slot, // we use the name of the outer function. let var_slot = U256::from_str(slot_value.as_str().unwrap()).unwrap(); - if !struct_slots.contains_key(&var_slot) { - struct_slots.insert( - var_slot, - ( - struct_id, - Some(format!("[{}]", outer_function)) - ) - ); - } + struct_slots.entry(var_slot).or_insert((struct_id, Some(outer_function))); } } } @@ -818,12 +808,12 @@ impl ProjectInfo { .unwrap(), ) .unwrap(); - if !struct_slots.contains_key(&var_slot) { - struct_slots.insert( - var_slot, - (struct_id, function_name) - ); - } + struct_slots + .entry(var_slot) + .or_insert(( + struct_id, + function_name, + )); } } } diff --git a/src/dvf.rs b/src/dvf.rs index 9d3eb8a..e3cff68 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -1006,7 +1006,10 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { // Extend storage with implementation storage variables, ensuring unique slots for storage_var in &tmp_project_info.storage { - if !storage.iter().any(|existing| existing.slot == storage_var.slot) { + if !storage + .iter() + .any(|existing| existing.slot == storage_var.slot) + { storage.push(storage_var.clone()); } } From c684037570ded809f48425f4f53f3dddc69cc6e1 Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Fri, 4 Jul 2025 10:36:46 +0200 Subject: [PATCH 4/5] fixed ordering --- lib/bytecode_verification/parse_json.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/bytecode_verification/parse_json.rs b/lib/bytecode_verification/parse_json.rs index c5054ff..1258602 100644 --- a/lib/bytecode_verification/parse_json.rs +++ b/lib/bytecode_verification/parse_json.rs @@ -326,7 +326,7 @@ impl ProjectInfo { .unwrap() .to_string(); if identifier.starts_with("t_struct") { - let struct_slots: HashMap)> = HashMap::from([( + let struct_slots_vec: Vec<(U256, (u64, Option))> = Vec::from([( U256::from_str("0x0").unwrap(), // this won't be used as we only have to add the types ( type_name @@ -345,7 +345,7 @@ impl ProjectInfo { sources, top_node, type_defs, - &struct_slots, + &struct_slots_vec, types, &mut storage, ); @@ -442,7 +442,7 @@ impl ProjectInfo { sources: &BTreeMap, node: &EAstNode, type_defs: &Types, - struct_slots: &HashMap)>, + struct_slots: &Vec<(U256, (u64, Option))>, types: &mut HashMap, storage: &mut Vec, ) { @@ -604,6 +604,11 @@ impl ProjectInfo { } } } + + // Order struct_slots by key for deterministic results + let mut struct_slots_vec: Vec<(U256, (u64, Option))> = struct_slots.iter().map(|(k, v)| (*k, v.clone())).collect(); + struct_slots_vec.sort_by(|a, b| a.0.cmp(&b.0)); + // parse the struct members + types for source in sources.values() { if let Some(ast) = source.ast.clone() { @@ -612,7 +617,7 @@ impl ProjectInfo { sources, node, type_defs, - &struct_slots, + &struct_slots_vec, types, storage, ); From d03d90963703bf4d02410967e9cac9b89085d9e1 Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Fri, 4 Jul 2025 11:25:32 +0200 Subject: [PATCH 5/5] fixed test case file --- lib/bytecode_verification/compare_bytecodes.rs | 2 +- lib/bytecode_verification/parse_json.rs | 3 ++- lib/bytecode_verification/verify_bytecode.rs | 16 ++++------------ src/dvf.rs | 4 ++-- tests/expected_dvfs/CrazyHiddenStruct.dvf.json | 4 ++-- 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/bytecode_verification/compare_bytecodes.rs b/lib/bytecode_verification/compare_bytecodes.rs index 6553dec..876d700 100644 --- a/lib/bytecode_verification/compare_bytecodes.rs +++ b/lib/bytecode_verification/compare_bytecodes.rs @@ -297,7 +297,7 @@ impl CompareInitCode { for (arg, value) in project_info.constructor_args.iter_mut().zip(&decoded_args) { let encoded_value = value.abi_encode_packed(); - if encoded_value.len() == 0 { + if encoded_value.is_empty() { // Here we keep the arg.type_string we previous extracted from the ABI // This happens with empty arrays arg.value = "0x".to_string(); diff --git a/lib/bytecode_verification/parse_json.rs b/lib/bytecode_verification/parse_json.rs index 1258602..db28c4f 100644 --- a/lib/bytecode_verification/parse_json.rs +++ b/lib/bytecode_verification/parse_json.rs @@ -606,7 +606,8 @@ impl ProjectInfo { } // Order struct_slots by key for deterministic results - let mut struct_slots_vec: Vec<(U256, (u64, Option))> = struct_slots.iter().map(|(k, v)| (*k, v.clone())).collect(); + let mut struct_slots_vec: Vec<(U256, (u64, Option))> = + struct_slots.iter().map(|(k, v)| (*k, v.clone())).collect(); struct_slots_vec.sort_by(|a, b| a.0.cmp(&b.0)); // parse the struct members + types diff --git a/lib/bytecode_verification/verify_bytecode.rs b/lib/bytecode_verification/verify_bytecode.rs index 5f0b05d..782af68 100644 --- a/lib/bytecode_verification/verify_bytecode.rs +++ b/lib/bytecode_verification/verify_bytecode.rs @@ -24,7 +24,7 @@ pub fn print_verification_summary( contract_address: &Address, status: CompareBytecode, project_info: &ProjectInfo, - on_chain_bytecode: &String, + on_chain_bytecode: &str, ) { let mut table = Table::new(); @@ -70,11 +70,7 @@ pub fn print_verification_summary( table.printstd(); } -pub fn write_out_bytecodes( - project_info: &ProjectInfo, - on_chain_bytecode: &String, - table: &mut Table, -) { +pub fn write_out_bytecodes(project_info: &ProjectInfo, on_chain_bytecode: &str, table: &mut Table) { let mut compiled_file = File::create("compiled_bytecode.txt").expect("Could not create file"); let mut on_chain_file = File::create("on_chain_bytecode.txt").expect("Could not create file"); @@ -96,11 +92,7 @@ pub fn write_out_bytecodes( ]); } -pub fn write_out_initcodes( - project_info: &ProjectInfo, - on_chain_initcode: &String, - table: &mut Table, -) { +pub fn write_out_initcodes(project_info: &ProjectInfo, on_chain_initcode: &str, table: &mut Table) { let mut compiled_file = File::create("compiled_initcode.txt").expect("Could not create file"); let mut on_chain_file = File::create("on_chain_initcode.txt").expect("Could not create file"); @@ -123,7 +115,7 @@ pub fn print_generation_summary( contract_address: &Address, status: CompareBytecode, project_info: &ProjectInfo, - on_chain_bytecode: &String, + on_chain_bytecode: &str, pretty_printer: &PrettyPrinter, ) { let mut table = Table::new(); diff --git a/src/dvf.rs b/src/dvf.rs index e3cff68..bf79746 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -164,7 +164,7 @@ fn validate_dvf( return Err(ValidationError::from("Different codehash.")); } - let pretty_printer = PrettyPrinter::new(&config, Some(®istry)); + let pretty_printer = PrettyPrinter::new(config, Some(registry)); // Validate Storage slots print_progress("Validating Storage Variables.", &mut pc, &progress_mode); @@ -181,7 +181,7 @@ fn validate_dvf( if !storage_variable.compare(¤t_val[start_index..end_index]) { let message = get_mismatch_msg( &pretty_printer, - &storage_variable, + storage_variable, ¤t_val[start_index..end_index], ); if continue_on_mismatch { diff --git a/tests/expected_dvfs/CrazyHiddenStruct.dvf.json b/tests/expected_dvfs/CrazyHiddenStruct.dvf.json index 9fcc21a..cf2bd21 100644 --- a/tests/expected_dvfs/CrazyHiddenStruct.dvf.json +++ b/tests/expected_dvfs/CrazyHiddenStruct.dvf.json @@ -1,6 +1,6 @@ { "version": "0.9.1", - "id": "0x71f27bd042cf53e6783d03336d86171f7d728e61f867e60071ae32818de10da2", + "id": "0x294a145622ea4fa416a4fe6395b3a307e7d692e6956c2fd0e2192831c7e776fa", "contract_name": "CrazyHiddenStruct", "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "chain_id": 1337, @@ -484,7 +484,7 @@ { "slot": "0xa83659c989cfe332581a2ed207e0e6d23d9199b0de773442a1e23a9b8c5138f0", "offset": 0, - "var_name": "CrazyHiddenStruct.[_setOwner].AddressSlot.value", + "var_name": "CrazyHiddenStruct._setOwner.AddressSlot.value", "var_type": "t_address", "value": "0x5fbdb2315678afecb367f032d93f642f64180aa3", "comparison_operator": "Equal"