diff --git a/CHANGELOG.md b/CHANGELOG.md index bdc475f65ef..fca2070a775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Unreleased] +### Added +- Add integration test for arithmetic overflow checks - [#2631](https://github.com/use-ink/ink/pull/2631) + ## Version 6.0.0-alpha.3 Compatibility of this release: diff --git a/integration-tests/internal/overflow-safety/Cargo.toml b/integration-tests/internal/overflow-safety/Cargo.toml new file mode 100644 index 00000000000..a0407dbb4f2 --- /dev/null +++ b/integration-tests/internal/overflow-safety/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "overflow-safety" +version = "6.0.0-alpha.3" +authors = ["Use Ink "] +edition = "2024" +publish = false + +[dependencies] +ink = { path = "../../../crates/ink", default-features = false } + +[dev-dependencies] +ink_e2e = { path = "../../../crates/e2e" } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", +] +ink-as-dependency = [] +e2e-tests = [] + +[package.metadata.ink-lang] +abi = "ink" diff --git a/integration-tests/internal/overflow-safety/lib.rs b/integration-tests/internal/overflow-safety/lib.rs new file mode 100644 index 00000000000..98545ecf47e --- /dev/null +++ b/integration-tests/internal/overflow-safety/lib.rs @@ -0,0 +1,167 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[ink::contract] +pub mod overflow_safety { + #[ink(storage)] + pub struct OverflowSafety {} + + impl OverflowSafety { + /// Creates a new smart contract. + #[ink(constructor)] + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self {} + } + + /// Adds the given values. + /// + /// # Note + /// + /// Should panic on overflow. + #[ink(message)] + pub fn add(&self, a: u8, b: u8) -> u8 { + a + b + } + + /// Subtracts the given values. + /// + /// # Note + /// + /// Should panic on overflow. + #[ink(message)] + pub fn sub(&self, a: u8, b: u8) -> u8 { + a - b + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[ink::test] + fn add_no_overflow_works() { + let overflow_safety = OverflowSafety::new(); + assert_eq!(overflow_safety.add(100u8, 50u8), 150u8); + } + + #[ink::test] + #[should_panic(expected = "attempt to add with overflow")] + fn add_with_overflow_panics() { + let overflow_safety = OverflowSafety::new(); + overflow_safety.add(u8::MAX, 1u8); + } + + #[ink::test] + fn sub_no_overflow_works() { + let overflow_safety = OverflowSafety::new(); + assert_eq!(overflow_safety.sub(100u8, 50u8), 50u8); + } + + #[ink::test] + #[should_panic(expected = "attempt to subtract with overflow")] + fn sub_with_overflow_panics() { + let overflow_safety = OverflowSafety::new(); + overflow_safety.sub(u8::MIN, 1u8); + } + } + + #[cfg(all(test, feature = "e2e-tests"))] + mod e2e_tests { + use super::*; + use ink_e2e::ContractsBackend; + + type E2EResult = std::result::Result>; + + #[ink_e2e::test] + async fn add_no_overflow_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = OverflowSafetyRef::new(); + let contract = client + .instantiate("overflow_safety", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // when + let add = call_builder.add(100u8, 50u8); + let add_res = client + .call(&ink_e2e::bob(), &add) + .submit() + .await + .expect("add failed"); + assert_eq!(add_res.return_value(), 150u8); + + Ok(()) + } + + #[ink_e2e::test] + async fn add_with_overflow_reverts( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = OverflowSafetyRef::new(); + let contract = client + .instantiate("overflow_safety", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // when + let add = call_builder.add(u8::MAX, 1u8); + let add_res = client.call(&ink_e2e::bob(), &add).submit().await; + assert!(matches!(add_res, Err(ink_e2e::Error::CallExtrinsic(_)))); + + Ok(()) + } + + #[ink_e2e::test] + async fn sub_no_overflow_works( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = OverflowSafetyRef::new(); + let contract = client + .instantiate("overflow_safety", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // when + let sub = call_builder.sub(100u8, 50u8); + let sub_res = client + .call(&ink_e2e::bob(), &sub) + .submit() + .await + .expect("add failed"); + assert_eq!(sub_res.return_value(), 50u8); + + Ok(()) + } + + #[ink_e2e::test] + async fn sub_with_overflow_reverts( + mut client: Client, + ) -> E2EResult<()> { + // given + let mut constructor = OverflowSafetyRef::new(); + let contract = client + .instantiate("overflow_safety", &ink_e2e::bob(), &mut constructor) + .submit() + .await + .expect("instantiate failed"); + let call_builder = contract.call_builder::(); + + // when + let sub = call_builder.sub(u8::MIN, 1u8); + let sub_res = client.call(&ink_e2e::bob(), &sub).submit().await; + assert!(matches!(sub_res, Err(ink_e2e::Error::CallExtrinsic(_)))); + + Ok(()) + } + } +}