diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index 62349d73..9d92df24 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -7,6 +7,7 @@ members = [ "counter", "debugger", "double_counter", + "empty_constructor", "eventer", "everest", "fallible_counter", diff --git a/contracts/empty_constructor/Cargo.toml b/contracts/empty_constructor/Cargo.toml new file mode 100644 index 00000000..209755a4 --- /dev/null +++ b/contracts/empty_constructor/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "empty_constructor" +version = "0.1.0" +authors = [ + "Eduardo Leegwater Simões ", +] +edition = "2021" + +license = "MPL-2.0" + +[dependencies] +piecrust-uplink = { path = "../../piecrust-uplink", features = ["abi", "dlmalloc"] } + +[lib] +crate-type = ["cdylib", "rlib"] diff --git a/contracts/empty_constructor/src/lib.rs b/contracts/empty_constructor/src/lib.rs new file mode 100644 index 00000000..4b89053a --- /dev/null +++ b/contracts/empty_constructor/src/lib.rs @@ -0,0 +1,56 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +//! Contract that provides and example use of the constructor. + +#![no_std] + +use piecrust_uplink as uplink; + +/// Struct that describes the state of the Constructor contract +pub struct EmptyConstructor { + value: u8, +} + +impl EmptyConstructor { + pub fn init(&mut self) { + self.value = 0x10; + } +} + +/// State of the EmptyConstructor contract +static mut STATE: EmptyConstructor = EmptyConstructor { value: 0x00 }; + +impl EmptyConstructor { + /// Read the value of the constructor contract state + pub fn read_value(&self) -> u8 { + self.value + } + + /// Increment the value by 1 + pub fn increment(&mut self) { + let value = self.value + 1; + self.value = value; + } +} + +/// Expose `EmptyConstructor::read_value()` to the host +#[no_mangle] +unsafe fn read_value(arg_len: u32) -> u32 { + uplink::wrap_call(arg_len, |_: ()| STATE.read_value()) +} + +/// Expose `EmptyConstructor::increment()` to the host +#[no_mangle] +unsafe fn increment(arg_len: u32) -> u32 { + uplink::wrap_call(arg_len, |_: ()| STATE.increment()) +} + +/// Expose `EmptyConstructor::init()` to the host +#[no_mangle] +unsafe fn init(arg_len: u32) -> u32 { + uplink::wrap_call(arg_len, |()| STATE.init()) +} diff --git a/piecrust/CHANGELOG.md b/piecrust/CHANGELOG.md index 2f03da4f..6310786c 100644 --- a/piecrust/CHANGELOG.md +++ b/piecrust/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Use empty constructor arguments by default [#316] - Upgrade `dusk-wasmtime` to version `18` ## [0.16.0] - 2024-02-14 @@ -366,6 +367,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#325]: https://github.com/dusk-network/piecrust/issues/325 [#324]: https://github.com/dusk-network/piecrust/issues/324 +[#316]: https://github.com/dusk-network/piecrust/issues/316 [#301]: https://github.com/dusk-network/piecrust/issues/313 [#301]: https://github.com/dusk-network/piecrust/issues/301 [#296]: https://github.com/dusk-network/piecrust/issues/296 diff --git a/piecrust/src/session.rs b/piecrust/src/session.rs index 8090f347..0fe513bb 100644 --- a/piecrust/src/session.rs +++ b/piecrust/src/session.rs @@ -289,22 +289,13 @@ impl Session { let instance = self.instance(&contract_id).expect("instance should exist"); - let has_init = instance.is_function_exported(INIT_METHOD); - if has_init && arg.is_none() { - return Err(InitalizationError( - "Contract has constructor but no argument was provided" - .into(), - )); - } - - if let Some(arg) = arg { - if !has_init { - return Err(InitalizationError( - "Argument was provided but contract has no constructor" - .into(), - )); - } - + if instance.is_function_exported(INIT_METHOD) { + // If no argument was provided, we call the constructor anyway, + // but with an empty argument. The alternative is to panic, but + // that assumes that the caller of `deploy` knows that the + // contract has a constructor in the first place, which might + // not be the case, such as when ingesting untrusted bytecode. + let arg = arg.unwrap_or_default(); self.call_inner(contract_id, INIT_METHOD, arg, gas_limit)?; } diff --git a/piecrust/tests/constructor.rs b/piecrust/tests/constructor.rs index 6018e5fc..7b8b9d5c 100644 --- a/piecrust/tests/constructor.rs +++ b/piecrust/tests/constructor.rs @@ -73,20 +73,20 @@ fn constructor() -> Result<(), Error> { } #[test] -fn missing_init() -> Result<(), Error> { +fn empty_constructor_argument() -> Result<(), Error> { let vm = VM::ephemeral()?; let mut session = vm.session(SessionData::builder())?; - let result = session.deploy( - contract_bytecode!("counter"), - ContractData::builder(OWNER).constructor_arg(&0xabu8), + let id = session.deploy( + contract_bytecode!("empty_constructor"), + ContractData::builder(OWNER), LIMIT, - ); + )?; - assert!( - result.is_err(), - "deploy with data when the 'init' method is not exported should fail with an error" + assert_eq!( + session.call::<_, u8>(id, "read_value", &(), LIMIT)?.data, + 0x10 ); Ok(())