Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,11 @@ dv init --project <PROJECT_PATH> --address <ADDRESS> --contractname <NAME> --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

Expand Down
4 changes: 2 additions & 2 deletions lib/dvf/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,11 +1080,11 @@ 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.",
)),
}
}
Expand Down
134 changes: 98 additions & 36 deletions lib/web3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down Expand Up @@ -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<Address> = vec![];
extract_create_addresses_from_call_frame(&call_frame, &mut addresses, false)?;
Expand All @@ -710,38 +710,76 @@ 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
};

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));
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
#[allow(clippy::type_complexity)]
let attempts: Vec<Box<dyn Fn() -> 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) => Ok(result),
Err(e) => {
debug!("Deployment search attempt failed: {}", e);
get_deployment_from_geth_trace(
config,
address,
start_block_num,
current_block_num,
)
}
}
}),
];

for attempt in attempts {
match attempt() {
Ok(result) => return Ok(result),
Err(e) => {
debug!("Deployment search attempt failed: {}", e);
}
}
}

Expand Down Expand Up @@ -2235,3 +2273,27 @@ mod tests {
// }
// }
}

pub fn get_eth_transaction_block_number(
config: &DVFConfig,
tx_hash: &str,
) -> Result<u64, ValidationError> {
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)
}
15 changes: 13 additions & 2 deletions src/dvf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,11 @@ fn main() {
.help("Name of the contract")
.required(true),
)
.arg(
arg!(--deployment <TX>)
.help("Transaction hash of the deployment transaction")
.value_parser(is_valid_32_byte_hex),
)
.arg(
arg!(--implementation <NAME>)
.help("Optional name of the implementation contract"),
Expand Down Expand Up @@ -757,6 +762,7 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> {
.get_many::<Vec<B256>>("eventtopics")
.map(|v| v.flat_map(|x| x.clone()).collect::<Vec<_>>());
let zerovalue = sub_m.get_flag("zerovalue");
let user_deployment_tx = sub_m.get_one::<String>("deployment");

let mut imp_env = *sub_m.get_one::<Environment>("implementationenv").unwrap();
let imp_project = sub_m.get_one::<PathBuf>("implementationproject");
Expand Down Expand Up @@ -792,8 +798,13 @@ fn process(matches: ArgMatches) -> Result<(), ValidationError> {
let pretty_printer = PrettyPrinter::new(&config, Some(&registry));

// 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;
Expand Down
Loading