diff --git a/README.md b/README.md index 9f8d8a5..502bde0 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,14 @@ dv init --project --address
--contractname --ini Please note that `` must be equal to or larger than the deployment block of the contract. Additionally, it is recommended to use only block numbers of **finalized blocks** in order to prevent the DVF containing wrong data due to possible re-orgs in the future. +By default, `dv` looks at all transactions starting from the deployment block. If you want to restrict the transaction lookup to a later starting block (e.g., to skip transactions before a contract upgrade), you can pass the desired block number with `--startblock`: + +``` +dv init --project --address
--contractname --startblock new.dvf.json +``` + +`` must be equal to or larger than the deployment block and equal to or smaller than the init block. + Sometimes, you might want to know when a storage variable has been initialized before but then reset back to zero. You can add such variables to the DVF with the `--zerovalue` flag: ``` diff --git a/lib/dvf/discovery.rs b/lib/dvf/discovery.rs index b028fb9..f283f31 100644 --- a/lib/dvf/discovery.rs +++ b/lib/dvf/discovery.rs @@ -484,16 +484,21 @@ pub fn get_tx_hashes( let mut seen_events: Vec = vec![]; let tx_hashes: Vec = if let Some(event_topics) = &event_topics { print_progress("Obtaining past events and transactions.", pc, progress_mode); - seen_events = web3::get_eth_events( - config, - address, - start_block_num, - end_block_num, - event_topics, - )?; + for topic in event_topics { + let mut topic_events = web3::get_eth_events_paginated( + config, + address, + start_block_num, + end_block_num, + &vec![*topic], + )?; + seen_events.append(&mut topic_events); + } + let mut seen_hashes = HashSet::new(); seen_events .iter() .filter_map(|e| e.transaction_hash.map(|h| format!("{h:#x}"))) + .filter(|h| seen_hashes.insert(h.clone())) .collect() } else { print_progress("Obtaining past transactions.", pc, progress_mode); @@ -506,7 +511,7 @@ pub fn get_tx_hashes( pub fn create_discovery_params_for_init<'a>( config: &'a DVFConfig, dumped: &'a parse::CompleteDVF, - deployment_block_num: u64, + start_block_num: u64, init_block_num: u64, project: &'a PathBuf, artifacts: &'a str, @@ -523,7 +528,7 @@ pub fn create_discovery_params_for_init<'a>( config, contract_name: &dumped.contract_name, address: &dumped.address, - start_block_num: deployment_block_num, + start_block_num, end_block_num: init_block_num, project: Some(project), artifacts, diff --git a/lib/state/contract_state.rs b/lib/state/contract_state.rs index 781c57f..bb5c12f 100644 --- a/lib/state/contract_state.rs +++ b/lib/state/contract_state.rs @@ -263,7 +263,9 @@ impl<'a> ContractState<'a> { fn memory_as_string(memory: &Vec) -> String { let mut mem_string = String::new(); for mem in memory { - mem_string += mem; + // Some RPC nodes (e.g., Anvil) return memory chunks with a "0x" prefix; + // strip it so the concatenated string is pure hex. + mem_string += mem.strip_prefix("0x").unwrap_or(mem); } mem_string diff --git a/src/dvf.rs b/src/dvf.rs index 2e021a4..ea64af0 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -408,6 +408,11 @@ fn main() { .help("The block number at which the state snapshot should be taken.") .value_parser(is_valid_blocknum), ) + .arg( + arg!(--startblock ) + .help("The first block to look at for transactions (defaults to deployment block).") + .value_parser(is_valid_blocknum), + ) .arg( arg!(--project ) .help("Path to the root folder of the source code project") @@ -763,6 +768,12 @@ fn main() { .help("Folder containing the artifacts") .default_value("artifacts"), ) + .arg( + arg!(--libraries ...) + .help("Library specifiers in the form Path:Name:Address. Accepts comma-separated values or repeated flags") + .value_delimiter(',') + .action(clap::ArgAction::Append), + ) .arg(arg!(--buildcache ).help("Folder containing build-info files")), ) .subcommand( @@ -932,9 +943,28 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { dumped.deployment_block_num = deployment_block_num; dumped.deployment_tx = deployment_tx; + let start_block_num = *sub_m + .get_one::("startblock") + .unwrap_or(&deployment_block_num); + if start_block_num < deployment_block_num { + return Err(ValidationError::from( + "Start block must be greater than or equal to the deployment block.", + )); + } + + let default_init_block = if sub_m.contains_id("startblock") { + start_block_num + } else { + deployment_block_num + 1 + }; let init_block_num = *sub_m .get_one::("initblock") - .unwrap_or(&(deployment_block_num + 1)); + .unwrap_or(&default_init_block); + if init_block_num < start_block_num { + return Err(ValidationError::from( + "Init block must be greater than or equal to the start block.", + )); + } dumped.init_block_num = init_block_num; let mut pc = 1_u64; @@ -1030,7 +1060,7 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { } = discover_storage_and_events(create_discovery_params_for_init( &config, &dumped, - deployment_block_num, + start_block_num, init_block_num, project, artifacts, diff --git a/test1.json b/test1.json deleted file mode 100644 index e73753a..0000000 --- a/test1.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "version": "0.9.1", - "id": "0x9c82796f5193c4db0965d63046ed83572b2e8140f2be2f9587125b37e8d8710c", - "contract_name": "ConstructorFactory", - "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3", - "chain_id": 31337, - "deployment_block_num": 1, - "init_block_num": 2, - "deployment_tx": "0x9f852dc8e8b0ad7dee5533ba7854bb676f3f270a3720c7501bbd0a65b1194663", - "codehash": "0x035d6d745ec488636551786986451c4f4d368d1fd9facc2c4f11b8b3733f5aa9", - "insecure": false, - "immutables": [], - "constructor_args": [], - "critical_storage_variables": [ - { - "slot": "0x0", - "offset": 0, - "var_name": "children.length", - "var_type": "t_uint256", - "value": "0x0000000000000000000000000000000000000000000000000000000000000002", - "value_hint": "2", - "comparison_operator": "Equal" - }, - { - "slot": "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563", - "offset": 0, - "var_name": "children[0]", - "var_type": "t_address", - "value": "0xa16e02e87b7454126e5e10d957a927a7f5b5d2be", - "comparison_operator": "Equal" - }, - { - "slot": "0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e564", - "offset": 0, - "var_name": "children[1]", - "var_type": "t_address", - "value": "0xb7a5bd0345ef1cc5e66bf61bdec17d2461fbd968", - "comparison_operator": "Equal" - } - ], - "critical_events": [], - "unvalidated_metadata": { - "author_name": "Author", - "description": "System Description", - "hardfork": [ - "paris", - "shanghai" - ], - "audit_report": "https://example.org/report.pdf", - "source_url": "https://github.com/source/code", - "security_contact": "security@example.org" - } -} \ No newline at end of file