diff --git a/CHANGELOG.md b/CHANGELOG.md index b1ef098d3..2e0a46426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove check for compatible `scale` and `scale-info` versions - [#1370](https://github.com/paritytech/cargo-contract/pull/1370) - Add workspace support -[#1358](https://github.com/paritytech/cargo-contract/pull/1358) +### Fixed +- Do not allow to execute calls on immutable contract messages - [#1397](https://github.com/paritytech/cargo-contract/pull/1397) + ## [4.0.0-alpha] Replaces the yanked `3.1.0` due to issues with supporting *both* Rust versions < `1.70` diff --git a/crates/extrinsics/src/call.rs b/crates/extrinsics/src/call.rs index a670ec1dc..fdcb419f4 100644 --- a/crates/extrinsics/src/call.rs +++ b/crates/extrinsics/src/call.rs @@ -267,6 +267,23 @@ impl CallExec { &self, gas_limit: Option, ) -> Result { + if !self + .transcoder() + .metadata() + .spec() + .messages() + .iter() + .find(|msg| msg.label() == &self.message) + .expect("message exist after calling CallExec::done()") + .mutates() + { + let inner = anyhow!( + "Tried to execute a call on the immutable contract message '{}'. Please do a dry-run instead.", + &self.message + ); + return Err(inner.into()); + } + // use user specified values where provided, otherwise estimate let gas_limit = match gas_limit { Some(gas_limit) => gas_limit, diff --git a/crates/extrinsics/src/integration_tests.rs b/crates/extrinsics/src/integration_tests.rs index abd0fcadf..8bd7206c5 100644 --- a/crates/extrinsics/src/integration_tests.rs +++ b/crates/extrinsics/src/integration_tests.rs @@ -96,14 +96,14 @@ impl ContractsNodeProcess { ); let result = OnlineClient::new().await; if let Ok(client) = result { - break Ok(client) + break Ok(client); } if attempts < MAX_ATTEMPTS { attempts += 1; - continue + continue; } if let Err(err) = result { - break Err(err) + break Err(err); } }; match client { @@ -509,6 +509,13 @@ async fn api_build_upload_instantiate_call() { .to_string(); assert!(value.contains("true"), "{:#?}", value); + // call the contract on the immutable "get" message trying to execute + // this should fail because "get" is immutable + match call.call(None).await { + Err(crate::ErrorVariant::Generic(_)) => {} + _ => panic!("immutable call was not prevented"), + } + // call the contract // flip the value let call = CallCommandBuilder::default()