From 7c2d5958c08316f15e242310b4bfd4d3f1856421 Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Fri, 16 May 2025 14:58:37 +0200 Subject: [PATCH 1/3] added some improvements to get the deployment tx --- README.md | 6 ++- lib/dvf/config.rs | 8 +-- lib/web3.rs | 123 ++++++++++++++++++++++++++++++++-------------- src/dvf.rs | 14 +++++- 4 files changed, 104 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 5b3cc555..975c84dc 100644 --- a/README.md +++ b/README.md @@ -546,7 +546,11 @@ dv init --project --address
--contractname --eve ## Common Problems -This section will be updated soon. +Sometimes, it is possible that the `init` command cannot find a deployment transaction. In this case, you have the following options: + +- Add an Etherscan API key to your config. +- Add a Blockscout API key to your config. +- Supply the deployment transaction manually with `--deployment`. ## Getting Help diff --git a/lib/dvf/config.rs b/lib/dvf/config.rs index f91287c8..265408d3 100644 --- a/lib/dvf/config.rs +++ b/lib/dvf/config.rs @@ -1058,7 +1058,6 @@ impl DVFConfig { chain_id ))); } - // First try chain-specific config if let Some(chain_config) = self.etherscan_chain_configs.get(&chain_id) { if let Some(api_url) = &chain_config.api_url { @@ -1080,17 +1079,14 @@ impl DVFConfig { Some(active_chain) => match active_chain.etherscan_urls() { Some((api_url, _base_url)) => Ok(api_url.to_string()), None => Err(ValidationError::from( - "Invalid active chain. Cannot chose Etherscan API.", + "Invalid active chain. Cannot use Etherscan API.", )), }, None => Err(ValidationError::from( - "No active chain. Cannot chose Etherscan API.", + "No active chain. Cannot use Etherscan API.", )), } } - None => Err(ValidationError::from( - "No active chain. Cannot chose Etherscan API.", - )), } } diff --git a/lib/web3.rs b/lib/web3.rs index 3fdb058e..7be4d5d1 100644 --- a/lib/web3.rs +++ b/lib/web3.rs @@ -95,7 +95,7 @@ pub fn get_block_traces( let request_body = json!({ "jsonrpc": "2.0", "method": "trace_block", - "params": [block_num], + "params": [format!("{:#x}", block_num)], "id": 1 }); let result = send_blocking_web3_post(config, &request_body)?; @@ -687,10 +687,10 @@ fn get_deployment_from_geth_trace( ) -> Result<(u64, String), ValidationError> { debug!("Searching geth traces for deployment tx of {:?}", address); for i in start_block_num..end_block_num + 1 { - let block = get_eth_block_by_num(config, i, true)?; - if let Some(txs) = block.transactions.as_transactions() { + let block = get_eth_block_by_num(config, i, false)?; + if let Some(txs) = block.transactions.as_hashes() { for tx in txs { - let tx_hash = format!("{:#x}", tx.inner.tx_hash()); + let tx_hash = format!("{:#x}", tx); let call_frame = get_eth_debug_call_trace(config, &tx_hash)?; let mut addresses: Vec
= vec![]; extract_create_addresses_from_call_frame(&call_frame, &mut addresses, false)?; @@ -710,44 +710,67 @@ pub fn get_deployment( config: &DVFConfig, address: &Address, ) -> Result<(u64, String), ValidationError> { - // First try etherscan - if let Ok(deployment_tx) = get_deployment_tx_from_etherscan(config, address) { - let deployment_tx_hash = deployment_tx.tx_hash; - debug!("Etherscan Deployment Tx: {}", deployment_tx_hash); - let deployment_block_num = get_block_number_for_tx(config, deployment_tx_hash.as_str())?; - return Ok((deployment_block_num, deployment_tx_hash)); - } else if let Ok(deployment_tx_hash) = get_deployment_tx_from_blockscout(config, address) { - debug!("Blockscout Deployment Tx: {}", deployment_tx_hash); - let deployment_block_num = get_block_number_for_tx(config, deployment_tx_hash.as_str())?; - return Ok((deployment_block_num, deployment_tx_hash)); - } else if let Ok(creator) = get_ots_contract_creator(config, address) { - debug!("Otterscan Deployment Tx: {}", creator.tx_hash); - let deployment_block_num = get_block_number_for_tx(config, creator.tx_hash.as_str())?; - return Ok((deployment_block_num, creator.tx_hash)); - } else { - debug!("No deployment tx found in etherscan or blockscout, searching traces. "); - let current_block_num = get_eth_block_number(config)?; - let start_block_num = if current_block_num > 10 { - get_deployment_block_from_binary_search(config, address, current_block_num)? - } else { - 1 - }; + let config_ptr = config as *const DVFConfig; + let address_ptr = address as *const Address; + + // SAFETY: We only use these pointers for read-only access in closures + let attempts: Vec Result<(u64, String), ValidationError>>> = vec![ + Box::new(move || { + // SAFETY: config and address are valid for the lifetime of this function + let config = unsafe { &*config_ptr }; + let address = unsafe { &*address_ptr }; + let deployment_tx = get_deployment_tx_from_etherscan(config, address)?; + let deployment_tx_hash = deployment_tx.tx_hash; + debug!("Etherscan Deployment Tx: {}", deployment_tx_hash); + let deployment_block_num = get_block_number_for_tx(config, deployment_tx_hash.as_str())?; + Ok((deployment_block_num, deployment_tx_hash)) + }), + Box::new(move || { + let config = unsafe { &*config_ptr }; + let address = unsafe { &*address_ptr }; + let deployment_tx_hash = get_deployment_tx_from_blockscout(config, address)?; + debug!("Blockscout Deployment Tx: {}", deployment_tx_hash); + let deployment_block_num = get_block_number_for_tx(config, deployment_tx_hash.as_str())?; + Ok((deployment_block_num, deployment_tx_hash)) + }), + Box::new(move || { + let config = unsafe { &*config_ptr }; + let address = unsafe { &*address_ptr }; + let creator = get_ots_contract_creator(config, address)?; + debug!("Otterscan Deployment Tx: {}", creator.tx_hash); + let deployment_block_num = get_block_number_for_tx(config, creator.tx_hash.as_str())?; + Ok((deployment_block_num, creator.tx_hash)) + }), + Box::new(move || { + let config = unsafe { &*config_ptr }; + let address = unsafe { &*address_ptr }; + debug!("No deployment tx found in etherscan or blockscout, searching traces. "); + let current_block_num = get_eth_block_number(config)?; + let start_block_num = if current_block_num > 10 { + get_deployment_block_from_binary_search(config, address, current_block_num)? + } else { + 1 + }; + match get_deployment_from_parity_trace(config, address, start_block_num, current_block_num) { + Ok(result) => return Ok(result), + Err(e) => { + debug!("Deployment search attempt failed: {}", e); + get_deployment_from_geth_trace(config, address, start_block_num, current_block_num) + } + } + }) + ]; - if let Ok((deployment_block_num, deployment_tx_hash)) = - get_deployment_from_parity_trace(config, address, start_block_num, current_block_num) - { - return Ok((deployment_block_num, deployment_tx_hash)); - } - if let Ok((deployment_block_num, deployment_tx_hash)) = - get_deployment_from_geth_trace(config, address, start_block_num, current_block_num) - { - return Ok((deployment_block_num, deployment_tx_hash)); + for attempt in attempts { + match attempt() { + Ok(result) => return Ok(result), + Err(e) => { + debug!("Deployment search attempt failed: {}", e); + } } } - Err(ValidationError::from( - "Could not find deployment transaction.", - )) + Err(ValidationError::from("Could not find deployment transaction.")) } pub fn get_deployment_block_from_binary_search( @@ -2235,3 +2258,27 @@ mod tests { // } // } } + +pub fn get_eth_transaction_block_number( + config: &DVFConfig, + tx_hash: &str, +) -> Result { + let request_body = json!({ + "jsonrpc": "2.0", + "method": "eth_getTransactionReceipt", + "params": [tx_hash], + "id": 1 + }); + let result = send_blocking_web3_post(config, &request_body)?; + + // Extract the block number from the receipt + let block_number_hex = result + .get("blockNumber") + .and_then(|bn| bn.as_str()) + .ok_or_else(|| ValidationError::from("Block number not found in transaction receipt"))?; + + // Convert the hex block number to u64 + let receipt = u64::from_str_radix(block_number_hex.trim_start_matches("0x"), 16) + .map_err(|_| ValidationError::from("Failed to parse block number from hex"))?; + Ok(receipt) +} diff --git a/src/dvf.rs b/src/dvf.rs index f8d11be1..e01b4cf5 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -421,6 +421,11 @@ fn main() { .help("Name of the contract") .required(true), ) + .arg( + arg!(--deployment ) + .help("Transaction hash of the deployment transaction") + .value_parser(is_valid_32_byte_hex), + ) .arg( arg!(--implementation ) .help("Optional name of the implementation contract"), @@ -757,6 +762,7 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { .get_many::>("eventtopics") .map(|v| v.flat_map(|x| x.clone()).collect::>()); let zerovalue = sub_m.get_flag("zerovalue"); + let user_deployment_tx = sub_m.get_one::("deployment"); let mut imp_env = *sub_m.get_one::("implementationenv").unwrap(); let imp_project = sub_m.get_one::("implementationproject"); @@ -792,8 +798,12 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { let pretty_printer = PrettyPrinter::new(&config, Some(®istry)); // Parse optional initblock or take deployment_block_num + 1 - let (deployment_block_num, deployment_tx) = - web3::get_deployment(&config, &dumped.address)?; + let (deployment_block_num, deployment_tx) = if user_deployment_tx.is_some() { + let block_num = web3::get_eth_transaction_block_number(&config, user_deployment_tx.unwrap())?; + (block_num, user_deployment_tx.unwrap().clone()) + } else { + web3::get_deployment(&config, &dumped.address)? + }; info!("Deployment Block: {}", deployment_block_num); dumped.deployment_block_num = deployment_block_num; dumped.deployment_tx = deployment_tx; From 376bd8a43ff57edc90e7fbec24527175ecc49f3d Mon Sep 17 00:00:00 2001 From: Stefan Effenberger Date: Fri, 16 May 2025 15:09:46 +0200 Subject: [PATCH 2/3] fmt + clippy --- lib/web3.rs | 33 ++++++++++++++++++++++++--------- src/dvf.rs | 3 ++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/web3.rs b/lib/web3.rs index 7be4d5d1..73ad22d9 100644 --- a/lib/web3.rs +++ b/lib/web3.rs @@ -714,6 +714,7 @@ pub fn get_deployment( let address_ptr = address as *const Address; // SAFETY: We only use these pointers for read-only access in closures + #[allow(clippy::type_complexity)] let attempts: Vec Result<(u64, String), ValidationError>>> = vec![ Box::new(move || { // SAFETY: config and address are valid for the lifetime of this function @@ -722,7 +723,8 @@ pub fn get_deployment( let deployment_tx = get_deployment_tx_from_etherscan(config, address)?; let deployment_tx_hash = deployment_tx.tx_hash; debug!("Etherscan Deployment Tx: {}", deployment_tx_hash); - let deployment_block_num = get_block_number_for_tx(config, deployment_tx_hash.as_str())?; + let deployment_block_num = + get_block_number_for_tx(config, deployment_tx_hash.as_str())?; Ok((deployment_block_num, deployment_tx_hash)) }), Box::new(move || { @@ -730,7 +732,8 @@ pub fn get_deployment( let address = unsafe { &*address_ptr }; let deployment_tx_hash = get_deployment_tx_from_blockscout(config, address)?; debug!("Blockscout Deployment Tx: {}", deployment_tx_hash); - let deployment_block_num = get_block_number_for_tx(config, deployment_tx_hash.as_str())?; + let deployment_block_num = + get_block_number_for_tx(config, deployment_tx_hash.as_str())?; Ok((deployment_block_num, deployment_tx_hash)) }), Box::new(move || { @@ -751,14 +754,24 @@ pub fn get_deployment( } else { 1 }; - match get_deployment_from_parity_trace(config, address, start_block_num, current_block_num) { - Ok(result) => return Ok(result), + match get_deployment_from_parity_trace( + config, + address, + start_block_num, + current_block_num, + ) { + Ok(result) => Ok(result), Err(e) => { debug!("Deployment search attempt failed: {}", e); - get_deployment_from_geth_trace(config, address, start_block_num, current_block_num) + get_deployment_from_geth_trace( + config, + address, + start_block_num, + current_block_num, + ) } } - }) + }), ]; for attempt in attempts { @@ -770,7 +783,9 @@ pub fn get_deployment( } } - Err(ValidationError::from("Could not find deployment transaction.")) + Err(ValidationError::from( + "Could not find deployment transaction.", + )) } pub fn get_deployment_block_from_binary_search( @@ -2270,13 +2285,13 @@ pub fn get_eth_transaction_block_number( "id": 1 }); let result = send_blocking_web3_post(config, &request_body)?; - + // Extract the block number from the receipt let block_number_hex = result .get("blockNumber") .and_then(|bn| bn.as_str()) .ok_or_else(|| ValidationError::from("Block number not found in transaction receipt"))?; - + // Convert the hex block number to u64 let receipt = u64::from_str_radix(block_number_hex.trim_start_matches("0x"), 16) .map_err(|_| ValidationError::from("Failed to parse block number from hex"))?; diff --git a/src/dvf.rs b/src/dvf.rs index e01b4cf5..5ee1082c 100644 --- a/src/dvf.rs +++ b/src/dvf.rs @@ -799,7 +799,8 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> { // Parse optional initblock or take deployment_block_num + 1 let (deployment_block_num, deployment_tx) = if user_deployment_tx.is_some() { - let block_num = web3::get_eth_transaction_block_number(&config, user_deployment_tx.unwrap())?; + let block_num = + web3::get_eth_transaction_block_number(&config, user_deployment_tx.unwrap())?; (block_num, user_deployment_tx.unwrap().clone()) } else { web3::get_deployment(&config, &dumped.address)? From e6454c99f6494dda766f2516a8f39d97f6f3a462 Mon Sep 17 00:00:00 2001 From: Hubert Ritzdorf Date: Wed, 21 May 2025 12:01:23 +0200 Subject: [PATCH 3/3] messed up rebase --- lib/dvf/config.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/dvf/config.rs b/lib/dvf/config.rs index 265408d3..0a505b7e 100644 --- a/lib/dvf/config.rs +++ b/lib/dvf/config.rs @@ -1058,6 +1058,7 @@ impl DVFConfig { chain_id ))); } + // First try chain-specific config if let Some(chain_config) = self.etherscan_chain_configs.get(&chain_id) { if let Some(api_url) = &chain_config.api_url { @@ -1087,6 +1088,9 @@ impl DVFConfig { )), } } + None => Err(ValidationError::from( + "No active chain. Cannot chose Etherscan API.", + )), } }