diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 57cdca8999..cda00fae49 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -384,7 +384,11 @@ examples-test: cargo test --verbose --manifest-path ${example}/Cargo.toml --features "foo, bar"; fi; if grep -q "e2e-tests = \[\]" "${example}/Cargo.toml"; then - cargo test --verbose --manifest-path ${example}/Cargo.toml --features e2e-tests; + if [ "$example" = "integration-tests/static-buffer/" ]; then + cargo clean && INK_STATIC_BUFFER_SIZE=30 cargo test --verbose --manifest-path ${example}/Cargo.toml --features e2e-tests && cargo clean; + else + cargo test --verbose --manifest-path ${example}/Cargo.toml --features e2e-tests; + fi; else cargo test --verbose --manifest-path ${example}/Cargo.toml; fi; diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b1ea7a996..297b0ae5a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Stabilize `call_runtime` ‒ [#1749](https://github.com/paritytech/ink/pull/1749) - Make E2E testcases generic over `E2EBackend` trait - [#1867](https://github.com/paritytech/ink/pull/1867) +- Modify static buffer size via environmental variables - [#1869](https://github.com/paritytech/ink/pull/1869) ### Added - Schema generation - [#1765](https://github.com/paritytech/ink/pull/1765) diff --git a/Cargo.toml b/Cargo.toml index 330f90c9d0..a595793679 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,6 +74,7 @@ tracing-subscriber = { version = "0.3.17" } trybuild = { version = "1.0.60" } which = { version = "4.4.0" } xxhash-rust = { version = "0.8" } +const_env = { version = "0.1"} # Substrate dependencies pallet-contracts-primitives = { version = "24.0.0", default-features = false } diff --git a/crates/env/Cargo.toml b/crates/env/Cargo.toml index 240a0d5b86..28dec0bc14 100644 --- a/crates/env/Cargo.toml +++ b/crates/env/Cargo.toml @@ -28,6 +28,7 @@ cfg-if = { workspace = true } paste = { workspace = true } arrayref = { workspace = true } static_assertions = { workspace = true } +const_env = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] rlibc = "1" diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 88a7ba8e9d..b7b1e539ee 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -51,7 +51,7 @@ use ink_storage_traits::Storable; /// The capacity of the static buffer. /// This is the same size as the ink! on-chain environment. We chose to use the same size /// to be as close to the on-chain behavior as possible. -const BUFFER_SIZE: usize = 1 << 14; // 16 kB +const BUFFER_SIZE: usize = crate::BUFFER_SIZE; impl CryptoHash for Blake2x128 { fn hash(input: &[u8], output: &mut ::Type) { diff --git a/crates/env/src/engine/on_chain/buffer.rs b/crates/env/src/engine/on_chain/buffer.rs index 8cd59043a1..a6f0973437 100644 --- a/crates/env/src/engine/on_chain/buffer.rs +++ b/crates/env/src/engine/on_chain/buffer.rs @@ -12,15 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// A static buffer with 16 kB of capacity. +/// A static buffer of variable capacity. pub struct StaticBuffer { - /// The static buffer with a total capacity of 16 kB. + /// A static buffer of variable capacity. buffer: [u8; Self::CAPACITY], } impl StaticBuffer { /// The capacity of the static buffer. - const CAPACITY: usize = 1 << 14; // 16 kB + /// Usually set to 16 kB. + /// Can be modified by setting `INK_STATIC_BUFFER_SIZE` environmental variable. + const CAPACITY: usize = crate::BUFFER_SIZE; /// Creates a new static buffer. pub const fn new() -> Self { diff --git a/crates/env/src/lib.rs b/crates/env/src/lib.rs index 6aa0e44e3a..881de49344 100644 --- a/crates/env/src/lib.rs +++ b/crates/env/src/lib.rs @@ -46,6 +46,12 @@ unused_extern_crates )] +/// The capacity of the static buffer. +/// Usually set to 16 kB. +/// Can be modified by setting `INK_STATIC_BUFFER_SIZE` environmental variable. +#[const_env::from_env("INK_STATIC_BUFFER_SIZE")] +pub const BUFFER_SIZE: usize = 16384; + #[cfg(all(not(feature = "std"), target_arch = "wasm32"))] #[allow(unused_extern_crates)] extern crate rlibc; diff --git a/integration-tests/static-buffer/.gitignore b/integration-tests/static-buffer/.gitignore new file mode 100644 index 0000000000..bf910de10a --- /dev/null +++ b/integration-tests/static-buffer/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock \ No newline at end of file diff --git a/integration-tests/static-buffer/Cargo.toml b/integration-tests/static-buffer/Cargo.toml new file mode 100644 index 0000000000..0a2911165a --- /dev/null +++ b/integration-tests/static-buffer/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "static-buffer" +version = "4.2.0" +authors = ["Parity Technologies "] +edition = "2021" +publish = false + +[dependencies] +ink = { path = "../../crates/ink", default-features = false } + +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } + +[dev-dependencies] +ink_e2e = { path = "../../crates/e2e" } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/integration-tests/static-buffer/README.md b/integration-tests/static-buffer/README.md new file mode 100644 index 0000000000..eb4f58723d --- /dev/null +++ b/integration-tests/static-buffer/README.md @@ -0,0 +1,18 @@ +# Static buffer configuration demo + +This is a dummy contract illustrating how the [static buffer](/ARCHITECTURE.md#communication-with-the-pallet) +can be be configured using the environmental variables. + +Simply, run: +```bash +cargo clean +INK_STATIC_BUFFER_SIZE=30 cargo test -F e2e-tests +``` + +This will configure the buffer to have enough space to instantiate the contract, +but not enough space to retrieve the caller's address as it is of 32 bytes, +but we only allocated 30 bytes to the contract. + +## Note +You must run `cargo clean` every time you want to modify the buffer size +because the value is baked into the binaries. diff --git a/integration-tests/static-buffer/lib.rs b/integration-tests/static-buffer/lib.rs new file mode 100644 index 0000000000..d84321436c --- /dev/null +++ b/integration-tests/static-buffer/lib.rs @@ -0,0 +1,78 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[ink::contract] +pub mod static_buffer { + #[allow(unused_imports)] + use ink::env::BUFFER_SIZE; + #[ink(storage)] + pub struct StaticBuffer { + value: bool, + } + + impl StaticBuffer { + /// Creates a dummy smart contract. + #[ink(constructor)] + pub fn new(init_value: bool) -> Self { + Self { value: init_value } + } + + /// Sets a default value. + #[ink(constructor)] + pub fn new_default() -> Self { + Self::new(Default::default()) + } + + /// Returns the caller of the contract. + /// Should panic if the buffer size is less than 32 bytes. + #[ink(message)] + pub fn get_caller(&self) -> AccountId { + self.env().caller() + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[ink::test] + #[should_panic(expected = "the output buffer is too small!")] + fn run_out_buffer_memory() { + let flipper = StaticBuffer::new(false); + flipper.get_caller() + } + } + + #[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 e2e_run_out_of_buffer_memory( + mut client: Client, + ) -> E2EResult<()> { + // given + let constructor = StaticBufferRef::new(false); + let contract = client + .instantiate("static_buffer", &ink_e2e::alice(), constructor, 0, None) + .await + .expect("instantiate failed"); + let call = contract.call::(); + + // when + let get = call.get_caller(); + // then panics if `INK_STATIC_BUFFER_SIZE` is less than 32 bytes. + let res = client.call_dry_run(&ink_e2e::bob(), &get, 0, None).await; + println!("{}", super::BUFFER_SIZE); + assert!( + res.is_err(), + "Buffer size was larger than expected: {}", + super::BUFFER_SIZE.to_string() + ); + + Ok(()) + } + } +}