From 355aa15c613b75614124619009482ca0c5d71860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Fri, 22 Sep 2023 14:00:08 +0200 Subject: [PATCH] piecrust: add support for `panic` import --- piecrust/CHANGELOG.md | 11 ++++++++ piecrust/src/error.rs | 16 ++++++++++- piecrust/src/imports.rs | 23 ++++++++++++++- piecrust/src/session.rs | 23 ++++++++------- piecrust/src/store/memory.rs | 21 ++++++-------- piecrust/tests/counter.rs | 22 +++++++++++++++ piecrust/tests/fallible_counter.rs | 45 ------------------------------ 7 files changed, 92 insertions(+), 69 deletions(-) delete mode 100644 piecrust/tests/fallible_counter.rs diff --git a/piecrust/CHANGELOG.md b/piecrust/CHANGELOG.md index 83296de1..d85a1d03 100644 --- a/piecrust/CHANGELOG.md +++ b/piecrust/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `panic` import implementation [#271] +- Add `Error::ContractPanic` variant [#271] + +### Changed + +- Allow for multiple initializations on a new memory [#271] +- Downcast `Error::RuntimeError` on each call boundary [#271] + ## [0.10.0] - 2023-09-13 ### Added @@ -229,6 +239,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#234]: https://github.com/dusk-network/piecrust/pull/234 +[#271]: https://github.com/dusk-network/piecrust/issues/271 [#268]: https://github.com/dusk-network/piecrust/issues/268 [#254]: https://github.com/dusk-network/piecrust/issues/254 [#253]: https://github.com/dusk-network/piecrust/issues/253 diff --git a/piecrust/src/error.rs b/piecrust/src/error.rs index 79041cb2..2d3a44ec 100644 --- a/piecrust/src/error.rs +++ b/piecrust/src/error.rs @@ -70,6 +70,8 @@ pub enum Error { MissingHostQuery(String), #[error("OutOfPoints")] OutOfPoints, + #[error("Contract panic: {0}")] + ContractPanic(String), #[error(transparent)] ParsingError(wasmer::wasmparser::BinaryReaderError), #[error(transparent)] @@ -92,6 +94,18 @@ pub enum Error { ValidationError, } +impl Error { + pub fn normalize(self) -> Self { + match self { + Self::RuntimeError(rerr) => match rerr.downcast() { + Ok(err) => err, + Err(rerr) => Self::RuntimeError(rerr), + }, + err => err, + } + } +} + impl From for Error { fn from(err: Infallible) -> Self { Self::Infallible(err) @@ -162,9 +176,9 @@ const OTHER_STATUS_CODE: i32 = i32::MIN; impl From for ContractError { fn from(err: Error) -> Self { - // TODO implement this fully match err { Error::OutOfPoints => Self::OUTOFGAS, + Error::ContractPanic(_) => Self::PANIC, _ => Self::OTHER(OTHER_STATUS_CODE), } } diff --git a/piecrust/src/imports.rs b/piecrust/src/imports.rs index cc25b7f0..05c12bb4 100644 --- a/piecrust/src/imports.rs +++ b/piecrust/src/imports.rs @@ -33,6 +33,7 @@ impl DefaultImports { "feed" => Function::new_typed_with_env(store, &fenv, feed), "limit" => Function::new_typed_with_env(store, &fenv, limit), "spent" => Function::new_typed_with_env(store, &fenv, spent), + "panic" => Function::new_typed_with_env(store, &fenv, panic), "owner" => Function::new_typed_with_env(store, &fenv, owner), "self_id" => Function::new_typed_with_env(store, &fenv, self_id), } @@ -163,7 +164,9 @@ fn c( let arg = &arg_buf[..arg_len as usize]; callee.write_argument(arg); - let ret_len = callee.call(name, arg.len() as u32, callee_limit)?; + let ret_len = callee + .call(name, arg.len() as u32, callee_limit) + .map_err(Error::normalize)?; check_arg(callee, ret_len as u32)?; // copy back result @@ -339,6 +342,24 @@ fn spent(fenv: FunctionEnvMut) -> u64 { limit - remaining } +fn panic(fenv: FunctionEnvMut, arg_len: u32) -> Result<(), Error> { + let env = fenv.data(); + let instance = env.self_instance(); + + check_arg(instance, arg_len)?; + + instance.with_arg_buffer(|buf| { + let slice = &buf[..arg_len as usize]; + + let msg = match std::str::from_utf8(slice) { + Ok(msg) => msg, + Err(err) => return Err(Error::Utf8(err)), + }; + + Err(Error::ContractPanic(msg.to_owned())) + }) +} + fn owner(fenv: FunctionEnvMut) -> u32 { let env = fenv.data(); let self_id = env.self_contract_id(); diff --git a/piecrust/src/session.rs b/piecrust/src/session.rs index adac0e51..6251f88c 100644 --- a/piecrust/src/session.rs +++ b/piecrust/src/session.rs @@ -635,16 +635,19 @@ impl Session { })?; let arg_len = instance.write_bytes_to_arg_buffer(&fdata); - let ret_len = instance.call(fname, arg_len, limit).map_err(|err| { - if let Err(io_err) = self.revert_callstack() { - return Error::MemorySnapshotFailure { - reason: Some(Arc::new(err)), - io: Arc::new(io_err), - }; - } - self.pop_callstack_prune(); - err - })?; + let ret_len = instance + .call(fname, arg_len, limit) + .map_err(|err| { + if let Err(io_err) = self.revert_callstack() { + return Error::MemorySnapshotFailure { + reason: Some(Arc::new(err)), + io: Arc::new(io_err), + }; + } + self.pop_callstack_prune(); + err + }) + .map_err(Error::normalize)?; let ret = instance.read_bytes_from_arg_buffer(ret_len as u32); let spent = limit diff --git a/piecrust/src/store/memory.rs b/piecrust/src/store/memory.rs index 8913ba58..34090d89 100644 --- a/piecrust/src/store/memory.rs +++ b/piecrust/src/store/memory.rs @@ -28,7 +28,7 @@ pub const MAX_MEM_SIZE: usize = MAX_PAGES * PAGE_SIZE; pub(crate) struct MemoryInner { pub(crate) mmap: Mmap, pub(crate) def: VMMemoryDefinition, - init: bool, + is_new: bool, } /// WASM memory belonging to a given contract during a given session. @@ -50,7 +50,7 @@ impl Memory { inner: Arc::new(RwLock::new(MemoryInner { mmap, def, - init: false, + is_new: true, })), }) } @@ -79,7 +79,7 @@ impl Memory { inner: Arc::new(RwLock::new(MemoryInner { mmap, def, - init: true, + is_new: false, })), }) } @@ -201,16 +201,13 @@ impl LinearMemory for Memory { data: &[u8], ) -> Result<(), Trap> { let this = self.write(); - let mut inner = this.inner; - - match inner.init { - true => Ok(()), - false => { - initialize_memory_with_data(&inner.def, start, data).map(|_| { - inner.init = true; - }) - } + let inner = this.inner; + + if inner.is_new { + initialize_memory_with_data(&inner.def, start, data)?; } + + Ok(()) } } diff --git a/piecrust/tests/counter.rs b/piecrust/tests/counter.rs index ee453a0a..2cc92412 100644 --- a/piecrust/tests/counter.rs +++ b/piecrust/tests/counter.rs @@ -87,3 +87,25 @@ fn call_through_c() -> Result<(), Error> { Ok(()) } + +#[test] +fn increment_panic() -> Result<(), Error> { + let vm = VM::ephemeral()?; + + let mut session = vm.session(SessionData::builder())?; + + let counter_id = session.deploy( + contract_bytecode!("fallible_counter"), + ContractData::builder(OWNER), + LIMIT, + )?; + + match session.call::<_, ()>(counter_id, "increment", &true, LIMIT) { + Err(Error::ContractPanic(panic_msg)) => { + assert_eq!(panic_msg, String::from("Incremental panic")); + } + _ => panic!("Expected a panic error"), + } + + Ok(()) +} diff --git a/piecrust/tests/fallible_counter.rs b/piecrust/tests/fallible_counter.rs deleted file mode 100644 index 698be43e..00000000 --- a/piecrust/tests/fallible_counter.rs +++ /dev/null @@ -1,45 +0,0 @@ -// 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. - -use piecrust::{contract_bytecode, ContractData, Error, SessionData, VM}; - -const OWNER: [u8; 32] = [0u8; 32]; -const LIMIT: u64 = 1_000_000; - -#[test] -#[ignore] -fn fallible_read_write_panic() -> Result<(), Error> { - let vm = VM::ephemeral()?; - - let mut session = vm.session(SessionData::builder())?; - - let id = session.deploy( - contract_bytecode!("fallible_counter"), - ContractData::builder(OWNER), - LIMIT, - )?; - - session.call::<_, ()>(id, "increment", &false, LIMIT)?; - - assert_eq!( - session.call::<_, i64>(id, "read_value", &(), LIMIT)?.data, - 0xfd - ); - - let err = session - .call::<_, ()>(id, "increment", &true, LIMIT) - .is_err(); - - assert!(err, "execution failed"); - - assert_eq!( - session.call::<_, i64>(id, "read_value", &(), LIMIT)?.data, - 0xfd, - "should remain unchanged, since panics revert any changes" - ); - - Ok(()) -}