Skip to content

Commit abeefa3

Browse files
stiefnStefan Effenberger
andauthored
Re-execute transactions locally (#82)
* added ability to re-execute transactions locally * debug ci * debug ci * soem small fixes * updated rpc cache * updated github ci * fixed file paths * re-enabled all tests * updated readme * fixed block number and timestamp in tx simulation. simulation now only during mapping decoding possible * test fix --------- Co-authored-by: Stefan Effenberger <stefan.effenberger@chainsecurity.com>
1 parent 8cdd9d9 commit abeefa3

74 files changed

Lines changed: 479 additions & 83 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,12 @@ To successfully create DVFs for on-chain smart contracts, you need access to the
7676

7777
**Please note the following restrictions/requirements**:
7878

79-
1. While Blockscout and Etherscan API keys are optional, at least one of them is required to determine the deployment transaction of a contract. If you provide neither, you are limited to local RPC nodes with less than 100 blocks.
80-
2. A Blockscout API key allows for faster execution.
79+
1. The Blockscout API is required to fetch the transaction hashes of a contract. This API can only be omitted if you fetch transactions based on events.
80+
2. The Etherscan API is optional but can be used instead of Blockscout to fetch the deployment transaction of a contract.
8181
3. Your RPC node **must** support either `debug_traceTransaction` or `trace_transaction`.
82-
4. Your RPC node **should** support `debug_traceTransaction` with [opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#struct-opcode-logger) enabled. Otherwise, `dv` won't be able to decode mapping keys.
82+
4. For faster execution, your RPC node **may** support `debug_traceTransaction` with [opcode logger](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#struct-opcode-logger) enabled. Otherwise, `dv` will locally re-execute all transactions which might increase execution time.
8383
5. For faster execution, your RPC node **may** support `debug_storageRangeAt`.
8484

85-
The RPC provider [QuickNode](https://www.quicknode.com/) supports all aforementioned requirements. A full list of supported RPC providers may be added here at a later point in time.
86-
8785
To run `dv`, you can either [build from source](#building-from-source) it or use the pre-configured [Docker](#using-docker) image.
8886

8987
If you choose to install `dv`, the following dependencies have to be installed on your system:
@@ -614,9 +612,8 @@ This section will be updated soon.
614612
- Empty-string mapping keys can currently not be decoded correctly.
615613
- Big transaction traces (`debug_traceTransaction` with opcode logger) of multiple GB may cause a crash.
616614
- Proxy Contracts without events when changing the implementation cannot be accurately secured, as implementation changes could be missed.
617-
- Successfully running validation against an non-finalized block at height H does not guarantee, validity at height H.
615+
- Successfully running validation against a non-finalized block at height H does not guarantee validity at height H.
618616
- Missing optimizations can cause longer waiting times than necessary.
619-
- Celoscan.io is currently not supported.
620617

621618
## Supported Networks
622619

lib/bytecode_verification/compare_bytecodes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ impl CompareInitCode {
297297

298298
for (arg, value) in project_info.constructor_args.iter_mut().zip(&decoded_args) {
299299
let encoded_value = value.abi_encode_packed();
300-
if encoded_value.len() == 0 {
300+
if encoded_value.is_empty() {
301301
// Here we keep the arg.type_string we previous extracted from the ABI
302302
// This happens with empty arrays
303303
arg.value = "0x".to_string();

lib/bytecode_verification/verify_bytecode.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub fn print_verification_summary(
2424
contract_address: &Address,
2525
status: CompareBytecode,
2626
project_info: &ProjectInfo,
27-
on_chain_bytecode: &String,
27+
on_chain_bytecode: &str,
2828
) {
2929
let mut table = Table::new();
3030

@@ -70,11 +70,7 @@ pub fn print_verification_summary(
7070
table.printstd();
7171
}
7272

73-
pub fn write_out_bytecodes(
74-
project_info: &ProjectInfo,
75-
on_chain_bytecode: &String,
76-
table: &mut Table,
77-
) {
73+
pub fn write_out_bytecodes(project_info: &ProjectInfo, on_chain_bytecode: &str, table: &mut Table) {
7874
let mut compiled_file = File::create("compiled_bytecode.txt").expect("Could not create file");
7975
let mut on_chain_file = File::create("on_chain_bytecode.txt").expect("Could not create file");
8076

@@ -96,11 +92,7 @@ pub fn write_out_bytecodes(
9692
]);
9793
}
9894

99-
pub fn write_out_initcodes(
100-
project_info: &ProjectInfo,
101-
on_chain_initcode: &String,
102-
table: &mut Table,
103-
) {
95+
pub fn write_out_initcodes(project_info: &ProjectInfo, on_chain_initcode: &str, table: &mut Table) {
10496
let mut compiled_file = File::create("compiled_initcode.txt").expect("Could not create file");
10597
let mut on_chain_file = File::create("on_chain_initcode.txt").expect("Could not create file");
10698

@@ -123,7 +115,7 @@ pub fn print_generation_summary(
123115
contract_address: &Address,
124116
status: CompareBytecode,
125117
project_info: &ProjectInfo,
126-
on_chain_bytecode: &String,
118+
on_chain_bytecode: &str,
127119
pretty_printer: &PrettyPrinter,
128120
) {
129121
let mut table = Table::new();

lib/dvf/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ fn default_max_blocks() -> u64 {
129129
}
130130

131131
fn default_web3_timeout() -> u64 {
132-
700
132+
5000
133133
}
134134

135135
impl DVFConfig {

lib/dvf/discovery.rs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::utils::pretty::PrettyPrinter;
2020
use crate::utils::progress::{print_progress, ProgressMode};
2121
use crate::utils::read_write_file::get_project_paths;
2222
use crate::web3;
23+
use crate::web3::stop_anvil_instance;
2324

2425
pub struct DiscoveryParams<'a> {
2526
pub config: &'a DVFConfig,
@@ -79,12 +80,20 @@ pub fn discover_storage_and_events(
7980
let fi_layout = forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout(
8081
project_path,
8182
params.contract_name,
82-
project_info.absolute_path.clone(),
83+
if params.env == Environment::Hardhat {
84+
project_info.absolute_path.clone()
85+
} else {
86+
None
87+
},
8388
);
8489
let fi_ir = forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized(
8590
project_path,
8691
params.contract_name,
87-
project_info.absolute_path.clone(),
92+
if params.env == Environment::Hardhat {
93+
project_info.absolute_path.clone()
94+
} else {
95+
None
96+
},
8897
);
8998
let mut contract_state = ContractState::new_with_address(params.address, &pretty_printer);
9099
contract_state.add_forge_inspect(&fi_layout, &fi_ir);
@@ -140,12 +149,20 @@ pub fn discover_storage_and_events(
140149
let fi_impl_layout = forge_inspect::ForgeInspectLayoutStorage::generate_and_parse_layout(
141150
&imp_path,
142151
implementation_name,
143-
tmp_project_info.absolute_path.clone(),
152+
if params.implementation_env == Environment::Hardhat {
153+
tmp_project_info.absolute_path.clone()
154+
} else {
155+
None
156+
},
144157
);
145158
let fi_impl_ir = forge_inspect::ForgeInspectIrOptimized::generate_and_parse_ir_optimized(
146159
&imp_path,
147160
implementation_name,
148-
tmp_project_info.absolute_path.clone(),
161+
if params.implementation_env == Environment::Hardhat {
162+
tmp_project_info.absolute_path.clone()
163+
} else {
164+
None
165+
},
149166
);
150167
contract_state.add_forge_inspect(&fi_impl_layout, &fi_impl_ir);
151168

@@ -206,22 +223,25 @@ pub fn discover_storage_and_events(
206223
}
207224
seen_transactions.insert(tx_hash);
208225

209-
let mut found_trace = true;
210-
if let Ok(trace) = web3::get_eth_debug_trace(params.config, tx_hash) {
211-
if contract_state
212-
.record_traces(params.config, vec![trace])
213-
.is_err()
214-
{
215-
found_trace = false;
226+
info!("Getting trace for {}", tx_hash);
227+
match web3::get_eth_debug_trace_sim(params.config, tx_hash) {
228+
Ok((trace, anvil_config, anvil_instance)) => {
229+
let record_traces_config = match &anvil_config {
230+
Some(c) => c,
231+
None => params.config,
232+
};
233+
if let Err(err) = contract_state.record_traces(record_traces_config, vec![trace]) {
234+
missing_traces = true;
235+
info!("Warning. The trace for {tx_hash} cannot be obtained. Some mapping slots might not be decodable. You can try to increase the timeout in the config. Error: {}", err);
236+
}
237+
if let Some(anvil_instance) = anvil_instance {
238+
stop_anvil_instance(anvil_instance);
239+
}
240+
}
241+
Err(err) => {
216242
missing_traces = true;
243+
info!("Warning. The trace for {tx_hash} cannot be obtained. Some mapping slots might not be decodable. You can try to increase the timeout in the config. Error: {}", err);
217244
}
218-
} else {
219-
found_trace = false;
220-
missing_traces = true;
221-
}
222-
223-
if !found_trace {
224-
info!("Warning. The trace for {tx_hash} cannot be obtained. Some mapping slots might not be decodable.");
225245
}
226246
}
227247

lib/dvf/parse.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::num::ParseIntError;
1010
use std::path::Path;
1111

1212
use ruint;
13+
use tracing::info;
1314

1415
use crate::bytecode_verification::parse_json::ProjectInfo;
1516
use crate::utils::pretty::convert_bytes_to_i256;
@@ -128,23 +129,23 @@ impl From<serde_json::Error> for ValidationError {
128129
impl From<reqwest::Error> for ValidationError {
129130
fn from(error: reqwest::Error) -> Self {
130131
// Print the full error details
131-
eprintln!("Request failed: {:?}", error);
132+
info!("Request failed: {:?}", error);
132133

133134
// Optionally, print more specific causes
134135
if error.is_timeout() {
135-
eprintln!("Reason: Timeout");
136+
info!("Reason: Timeout");
136137
} else if error.is_connect() {
137-
eprintln!("Reason: Connection error");
138+
info!("Reason: Connection error");
138139
} else if error.is_status() {
139-
eprintln!("Reason: Received bad HTTP status");
140+
info!("Reason: Received bad HTTP status");
140141
} else if error.is_request() {
141-
eprintln!("Reason: Request failed to build");
142+
info!("Reason: Request failed to build");
142143
}
143144

144145
// Print source chain (if available)
145146
let mut source = error.source();
146147
while let Some(s) = source {
147-
eprintln!("Caused by: {}", s);
148+
info!("Caused by: {}", s);
148149
source = s.source();
149150
}
150151
ValidationError::Error(format!("Communication error occurred: {}", error))

0 commit comments

Comments
 (0)