diff --git a/prdoc/pr_10032.prdoc b/prdoc/pr_10032.prdoc new file mode 100644 index 000000000000..085bd210e5d9 --- /dev/null +++ b/prdoc/pr_10032.prdoc @@ -0,0 +1,13 @@ +title: 'pallet_revive: when a dry run simulates contract deployment, return the execution + result data.' +doc: +- audience: Runtime Dev + description: |- + Fixes https://github.com/paritytech/contract-issues/issues/177 + + Expose the deployed contract's runtime bytecode in eth_call responses during simulated contract creation. + + The test from issue https://github.com/paritytech/contract-issues/issues/177 sends an eth_call request without a destination address, while providing contract bytecode in the data field. This simulates a contract creation transaction. The test expects the RPC response to return the result of executing the init code, which is the deployed contract's runtime bytecode. While this result is not returned in actual deployments, it is expected in dry-run simulations. +crates: +- name: pallet-revive + bump: patch diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index f0b6ef0682cd..c9d8f53df940 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -1291,8 +1291,12 @@ where // if we are dealing with EVM bytecode // We upload the new runtime code, and update the code if !is_pvm { - // Only keep return data for tracing - let data = if crate::tracing::if_tracing(|_| {}).is_none() { + // Only keep return data for tracing and for dry runs. + // When a dry-run simulates contract deployment, keep the execution result's + // data. + let data = if crate::tracing::if_tracing(|_| {}).is_none() && + !self.exec_config.is_dry_run + { core::mem::replace(&mut output.data, Default::default()) } else { output.data.clone() diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 31e96769bef7..fc092d406a5a 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -1662,6 +1662,7 @@ impl Pallet { call_info.encoded_len, base_info.total_weight(), ) + .with_dry_run() }; // emulate transaction behavior @@ -2576,7 +2577,7 @@ macro_rules! impl_runtime_apis_plus_revive_traits { gas_limit.unwrap_or(blockweights.max_block), storage_deposit_limit.unwrap_or(u128::MAX), input_data, - $crate::ExecConfig::new_substrate_tx(), + $crate::ExecConfig::new_substrate_tx().with_dry_run(), ) } @@ -2602,7 +2603,7 @@ macro_rules! impl_runtime_apis_plus_revive_traits { code, data, salt, - $crate::ExecConfig::new_substrate_tx(), + $crate::ExecConfig::new_substrate_tx().with_dry_run(), ) } diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs index 48db56df0aca..6cb015b06e6c 100644 --- a/substrate/frame/revive/src/primitives.rs +++ b/substrate/frame/revive/src/primitives.rs @@ -348,12 +348,20 @@ pub struct ExecConfig { /// /// It is determined when transforming `eth_transact` into a proper extrinsic. pub effective_gas_price: Option, + /// Whether this configuration was created for a dry-run execution. + /// Use to enable logic that should only run in dry-run mode. + pub is_dry_run: bool, } impl ExecConfig { - /// Create a default config appropriate when the call originated from a subtrate tx. + /// Create a default config appropriate when the call originated from a substrate tx. pub fn new_substrate_tx() -> Self { - Self { bump_nonce: true, collect_deposit_from_hold: None, effective_gas_price: None } + Self { + bump_nonce: true, + collect_deposit_from_hold: None, + effective_gas_price: None, + is_dry_run: false, + } } /// Create a default config appropriate when the call originated from a ethereum tx. @@ -362,8 +370,15 @@ impl ExecConfig { bump_nonce: false, collect_deposit_from_hold: Some((encoded_len, base_weight)), effective_gas_price: Some(effective_gas_price), + is_dry_run: false, } } + + /// Set this config to be a dry-run. + pub fn with_dry_run(mut self) -> Self { + self.is_dry_run = true; + self + } } /// Indicates whether the code was removed after the last refcount was decremented.