diff --git a/Cargo.lock b/Cargo.lock index 4d71f07..2504a95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2434,18 +2434,18 @@ dependencies = [ [[package]] name = "polkavm" -version = "0.10.0" +version = "0.12.0" dependencies = [ "libc", "log", "polkavm-assembler", - "polkavm-common 0.10.0", + "polkavm-common 0.12.0", "polkavm-linux-raw", ] [[package]] name = "polkavm-assembler" -version = "0.10.0" +version = "0.12.0" dependencies = [ "log", ] @@ -2458,9 +2458,10 @@ checksum = "1d9428a5cfcc85c5d7b9fc4b6a18c4b802d0173d768182a51cc7751640f08b92" [[package]] name = "polkavm-common" -version = "0.10.0" +version = "0.12.0" dependencies = [ "log", + "polkavm-assembler", ] [[package]] @@ -2511,7 +2512,7 @@ dependencies = [ [[package]] name = "polkavm-linux-raw" -version = "0.10.0" +version = "0.12.0" [[package]] name = "ppv-lite86" @@ -4482,6 +4483,7 @@ version = "0.1.0" dependencies = [ "fortuples", "parity-scale-codec", + "polkavm", "scale-info", "tracing", "xcq-executor", diff --git a/poc/guests/Cargo.lock b/poc/guests/Cargo.lock index 50daaa8..7d5621c 100644 --- a/poc/guests/Cargo.lock +++ b/poc/guests/Cargo.lock @@ -153,18 +153,18 @@ dependencies = [ [[package]] name = "polkavm-common" -version = "0.10.0" +version = "0.12.0" [[package]] name = "polkavm-derive" -version = "0.10.0" +version = "0.12.0" dependencies = [ "polkavm-derive-impl-macro", ] [[package]] name = "polkavm-derive-impl" -version = "0.10.0" +version = "0.12.0" dependencies = [ "polkavm-common", "proc-macro2", @@ -174,7 +174,7 @@ dependencies = [ [[package]] name = "polkavm-derive-impl-macro" -version = "0.10.0" +version = "0.12.0" dependencies = [ "polkavm-derive-impl", "syn 2.0.63", diff --git a/poc/runtime/src/xcq.rs b/poc/runtime/src/xcq.rs index 7d2efcd..a02d1f8 100644 --- a/poc/runtime/src/xcq.rs +++ b/poc/runtime/src/xcq.rs @@ -119,7 +119,7 @@ mod tests { #[test] fn call_fungibles_hex() { - let raw_blob = include_bytes!("../../../output/poc-guest-test-xcq-api.polkavm"); + let raw_blob = include_bytes!("../../../output/poc-guest-sum-balance.polkavm"); let alice_public = sr25519::Pair::from_string("//Alice", None) .expect("static values are valid; qed") .public(); diff --git a/vendor/polkavm b/vendor/polkavm index 56af643..ada0db5 160000 --- a/vendor/polkavm +++ b/vendor/polkavm @@ -1 +1 @@ -Subproject commit 56af6433bd51208d3d516c1a094e73b753848b35 +Subproject commit ada0db579f2d0fb40cdbf259dd2f29eb434c1d21 diff --git a/xcq-executor/src/lib.rs b/xcq-executor/src/lib.rs index bf14375..386cc1a 100644 --- a/xcq-executor/src/lib.rs +++ b/xcq-executor/src/lib.rs @@ -6,44 +6,49 @@ pub use alloc::vec::Vec; pub use polkavm::{Caller, Config, Engine, Linker, Module, ProgramBlob}; pub trait XcqExecutorContext { - fn register_host_functions(&mut self, linker: &mut Linker); + type UserData; + type UserError; + fn register_host_functions(&mut self, linker: &mut Linker); + fn data(&mut self) -> &mut Self::UserData; } pub struct XcqExecutor { engine: Engine, - linker: Linker, + linker: Linker, context: Ctx, } #[derive(Debug)] -pub enum XcqExecutorError { - ProgramParseError(polkavm::ProgramParseError), - PrepareError(polkavm::Error), - ExecutionError(polkavm::ExecutionError), +pub enum XcqExecutorError { + MemoryAllocationError, + MemoryAccessError(polkavm::MemoryAccessError), + CallError(polkavm::CallError), + OtherPVMError(polkavm::Error), } -impl From for XcqExecutorError { - fn from(err: polkavm::ProgramParseError) -> Self { - Self::ProgramParseError(err) +impl From for XcqExecutorError { + fn from(err: polkavm::MemoryAccessError) -> Self { + Self::MemoryAccessError(err) } } -impl From for XcqExecutorError { +impl From for XcqExecutorError { fn from(err: polkavm::Error) -> Self { - Self::PrepareError(err) + Self::OtherPVMError(err) } } -impl From> for XcqExecutorError { - fn from(err: polkavm::ExecutionError) -> Self { - Self::ExecutionError(err) +impl From> for XcqExecutorError { + fn from(err: polkavm::CallError) -> Self { + Self::CallError(err) } } impl XcqExecutor { pub fn new(config: Config, mut context: Ctx) -> Self { let engine = Engine::new(&config).unwrap(); - let mut linker = Linker::::new(&engine); + let mut linker = Linker::::new(); + // Register user-defined host functions context.register_host_functions(&mut linker); Self { engine, @@ -55,39 +60,41 @@ impl XcqExecutor { pub fn execute( &mut self, raw_blob: &[u8], - method: impl AsRef<[u8]>, + method: &str, input: &[u8], - ) -> Result, XcqExecutorError> { - let blob = ProgramBlob::parse(raw_blob.into())?; + ) -> Result, XcqExecutorError> { + let blob = ProgramBlob::parse(raw_blob.into()).map_err(polkavm::Error::from)?; let module = Module::from_blob(&self.engine, &Default::default(), blob)?; let instance_pre = self.linker.instantiate_pre(&module)?; - let instance = instance_pre.instantiate()?; + let mut instance = instance_pre.instantiate()?; let input_ptr = if !input.is_empty() { // First sbrk call to get the start of the heap - let start_ptr = instance.sbrk(0)?.expect("should not fail because we don't allocate"); + let start_ptr = instance + .sbrk(0) + .expect("should not fail because we don't allocate") + .expect("should not fail because we don't allocate"); // Second sbrk call to check the allocation doesn't exceed the heap limit - if instance.sbrk(input.len() as u32)?.is_none() { - return Err(XcqExecutorError::ExecutionError(polkavm::ExecutionError::Error( - polkavm::Error::from("cannot srk enough memory"), - ))); - }; - // Args are passed via guest's heap instance - .write_memory(start_ptr, input) - .map_err(|e| XcqExecutorError::ExecutionError(polkavm::ExecutionError::Trap(e)))?; + .sbrk(input.len() as u32) + .map_err(|_| XcqExecutorError::MemoryAllocationError)? + .ok_or(XcqExecutorError::MemoryAllocationError)?; + // Args are passed via guest's heap + instance.write_memory(start_ptr, input)?; start_ptr } else { 0 }; tracing::info!("(passing args): input_ptr: {}, input_len: {:?}", input_ptr, input.len()); - let res = instance.call_typed::<(u32, u32), u64>(&mut self.context, method, (input_ptr, input.len() as u32))?; + let res = instance.call_typed_and_get_result::( + self.context.data(), + method, + (input_ptr, input.len() as u32), + )?; let res_size = (res >> 32) as u32; let res_ptr = (res & 0xffffffff) as u32; - let result = instance - .read_memory_into_vec(res_ptr, res_size) - .map_err(|e| XcqExecutorError::ExecutionError(polkavm::ExecutionError::Trap(e)))?; + let result = instance.read_memory(res_ptr, res_size)?; Ok(result) } } diff --git a/xcq-extension/Cargo.toml b/xcq-extension/Cargo.toml index f6633f0..e4da5a9 100644 --- a/xcq-extension/Cargo.toml +++ b/xcq-extension/Cargo.toml @@ -9,6 +9,7 @@ version.workspace = true [dependencies] parity-scale-codec = { workspace = true } +polkavm = { workspace = true } scale-info = { workspace = true } xcq-executor = { workspace = true } fortuples = { workspace = true } diff --git a/xcq-extension/src/error.rs b/xcq-extension/src/error.rs index 4e8a119..c7991f2 100644 --- a/xcq-extension/src/error.rs +++ b/xcq-extension/src/error.rs @@ -4,8 +4,15 @@ use parity_scale_codec::Error as CodeCError; #[derive(Debug)] pub enum ExtensionError { PermissionError, - PolkavmError, + MemoryAllocationError, + MemoryAccessError(polkavm::MemoryAccessError), DecodeError(CodeCError), DispatchError(DispatchError), UnsupportedExtension, } + +impl From for ExtensionError { + fn from(e: polkavm::MemoryAccessError) -> Self { + Self::MemoryAccessError(e) + } +} diff --git a/xcq-extension/src/lib.rs b/xcq-extension/src/lib.rs index 0efb67b..4bb160a 100644 --- a/xcq-extension/src/lib.rs +++ b/xcq-extension/src/lib.rs @@ -40,6 +40,7 @@ pub trait CallDataTuple { struct Context { invoke_source: InvokeSource, + user_data: (), _marker: PhantomData<(C, P)>, } @@ -47,70 +48,76 @@ impl Context { pub fn new(invoke_source: InvokeSource) -> Self { Self { invoke_source, + user_data: (), _marker: PhantomData, } } } impl XcqExecutorContext for Context { - fn register_host_functions(&mut self, linker: &mut Linker) { + type UserData = (); + type UserError = ExtensionError; + fn data(&mut self) -> &mut Self::UserData { + &mut self.user_data + } + fn register_host_functions(&mut self, linker: &mut Linker) { let invoke_source = self.invoke_source; linker - .func_wrap( + .define_typed( "host_call", - move |mut caller: Caller<_>, extension_id: u64, call_ptr: u32, call_len: u32| -> u64 { + move |caller: Caller<'_, Self::UserData>, + extension_id: u64, + call_ptr: u32, + call_len: u32| + -> Result { // useful closure to handle early return - let mut func_with_result = || -> Result { - let call_bytes = caller - .read_memory_into_vec(call_ptr, call_len) - .map_err(|_| ExtensionError::PolkavmError)?; - tracing::info!("(host call): call_ptr: {}, call_len: {:?}", call_ptr, call_len); - tracing::info!( - "(host call): extension_id: {}, call_bytes: {:?}", - extension_id, - call_bytes - ); - if !P::is_allowed(extension_id, &call_bytes, invoke_source) { - return Err(ExtensionError::PermissionError); - } - let res_bytes = C::dispatch(extension_id, &call_bytes)?; - tracing::debug!("(host call): res_bytes: {:?}", res_bytes); - let res_bytes_len = res_bytes.len(); - let res_ptr = caller.sbrk(0).ok_or(ExtensionError::PolkavmError)?; - if caller.sbrk(res_bytes_len as u32).is_none() { - return Err(ExtensionError::PolkavmError); - } - caller - .write_memory(res_ptr, &res_bytes) - .map_err(|_| ExtensionError::PolkavmError)?; - Ok(((res_bytes_len as u64) << 32) | (res_ptr as u64)) - }; - let result = func_with_result(); - tracing::trace!("(host call): result: {:?}", result); - result.unwrap_or(0) + let call_bytes = caller.instance.read_memory(call_ptr, call_len)?; + tracing::info!("(host call): call_ptr: {}, call_len: {:?}", call_ptr, call_len); + tracing::info!( + "(host call): extension_id: {}, call_bytes: {:?}", + extension_id, + call_bytes + ); + if !P::is_allowed(extension_id, &call_bytes, invoke_source) { + return Err(ExtensionError::PermissionError); + } + let res_bytes = C::dispatch(extension_id, &call_bytes)?; + tracing::debug!("(host call): res_bytes: {:?}", res_bytes); + let res_bytes_len = res_bytes.len(); + let res_ptr = caller + .instance + .sbrk(0) + .map_err(|_| ExtensionError::MemoryAllocationError)? + .ok_or(ExtensionError::MemoryAllocationError)?; + caller + .instance + .sbrk(res_bytes_len as u32) + .map_err(|_| ExtensionError::MemoryAllocationError)? + .ok_or(ExtensionError::MemoryAllocationError)?; + caller.instance.write_memory(res_ptr, &res_bytes)?; + Ok(((res_bytes_len as u64) << 32) | (res_ptr as u64)) }, ) .unwrap(); linker - .func_wrap( + .define_typed( "return_ty", - move |mut caller: Caller<_>, extension_id: u64, call_index: u32| -> u64 { - let mut func_with_result = || -> Result { - let res_bytes = C::return_ty(extension_id, call_index)?; - tracing::debug!("(host call): res_bytes: {:?}", res_bytes); - let res_bytes_len = res_bytes.len(); - let res_ptr = caller.sbrk(0).ok_or(ExtensionError::PolkavmError)?; - if caller.sbrk(res_bytes_len as u32).is_none() { - return Err(ExtensionError::PolkavmError); - } - caller - .write_memory(res_ptr, &res_bytes) - .map_err(|_| ExtensionError::PolkavmError)?; - Ok(((res_bytes_len as u64) << 32) | (res_ptr as u64)) - }; - let result = func_with_result(); - tracing::trace!("(host call): result: {:?}", result); - result.unwrap_or(0) + move |caller: Caller<_>, extension_id: u64, call_index: u32| -> Result { + let res_bytes = C::return_ty(extension_id, call_index)?; + tracing::debug!("(host call): res_bytes: {:?}", res_bytes); + let res_bytes_len = res_bytes.len(); + let res_ptr = caller + .instance + .sbrk(0) + .map_err(|_| ExtensionError::MemoryAllocationError)? + .ok_or(ExtensionError::MemoryAllocationError)?; + caller + .instance + .sbrk(res_bytes_len as u32) + .map_err(|_| ExtensionError::MemoryAllocationError)? + .ok_or(ExtensionError::MemoryAllocationError)?; + caller.instance.write_memory(res_ptr, &res_bytes)?; + Ok(((res_bytes_len as u64) << 32) | (res_ptr as u64)) }, ) .unwrap(); @@ -131,7 +138,7 @@ impl ExtensionsExecutor { #[allow(dead_code)] pub fn execute_method(&mut self, guest: G, input: I) -> XcqResult { self.executor - .execute(guest.program(), input.method(), input.args()) + .execute(guest.program(), &input.method(), input.args()) .map_err(|e| format!("{:?}", e)) } }