From b20c9d2cfaa9cca1529660039b3abfa2588937a7 Mon Sep 17 00:00:00 2001 From: Piotr Olszewski Date: Wed, 18 Oct 2023 09:35:22 +0200 Subject: [PATCH] feat: handling get_module*/get_resource calls Added functions which execute appropriate calls in the move-vm-backend providing: - getting module bytecode - getting resources from the module - getting module ABI (not implemented on the backend side) Note: get_resource call is returning Ok(None) even for the resource which should exist. Currently ignoring the test, but need to be investigated further. --- Cargo.toml | 1 + src/lib.rs | 44 +++++++++++ tests/assets/move/sources/Empty.move | 2 + tests/modules.rs | 108 +++++++++++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 tests/modules.rs diff --git a/Cargo.toml b/Cargo.toml index 15527c3..81aff60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ description = "MoveVM support pallet" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +bcs = { git = "https://github.com/eigerco/bcs.git", default-features = false, branch = "master" } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive",] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } diff --git a/src/lib.rs b/src/lib.rs index fad4e08..2618363 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,12 +15,18 @@ pub use weights::*; // The pallet is defined below. #[frame_support::pallet] pub mod pallet { + #[cfg(not(feature = "std"))] + extern crate alloc; + #[cfg(not(feature = "std"))] + use alloc::format; + use codec::{FullCodec, FullEncode}; use frame_support::{ dispatch::{DispatchResultWithPostInfo, PostDispatchInfo}, pallet_prelude::*, }; use frame_system::pallet_prelude::*; + use move_core_types::account_address::AccountAddress; use move_vm_backend::Mvm; use move_vm_types::gas::UnmeteredGasMeter; use sp_std::{default::Default, vec::Vec}; @@ -160,4 +166,42 @@ pub mod pallet { { type VmStorage = VMStorage; } + + impl Pallet { + // Internal helper for creating new MoveVM instance with StorageAdapter. + fn move_vm() -> Result>>, Vec> { + let storage = Self::move_vm_storage(); + + Mvm::new(storage).map_err::, _>(|err| { + format!("error while creating the vm {:?}", err).into() + }) + } + + pub fn get_module_abi(module_id: &[u8]) -> Result>, Vec> { + let vm = Self::move_vm()?; + + vm.get_module_abi(module_id) + .map_err(|e| format!("error in get_module_abi: {:?}", e).into()) + } + + pub fn get_module(module_id: &[u8]) -> Result>, Vec> { + let vm = Self::move_vm()?; + + vm.get_module(module_id) + .map_err(|e| format!("error in get_module: {:?}", e).into()) + } + + pub fn get_resource( + account: &T::AccountId, + tag: &[u8], + ) -> Result>, Vec> { + let vm = Self::move_vm()?; + + vm.get_resource( + &AccountAddress::new(address::account_to_bytes(account)), + tag, + ) + .map_err(|e| format!("error in get_resource: {:?}", e).into()) + } + } } diff --git a/tests/assets/move/sources/Empty.move b/tests/assets/move/sources/Empty.move index b7098a3..f66c363 100644 --- a/tests/assets/move/sources/Empty.move +++ b/tests/assets/move/sources/Empty.move @@ -1,2 +1,4 @@ module TestAccount::Empty { + struct EmptyStruct { + } } diff --git a/tests/modules.rs b/tests/modules.rs new file mode 100644 index 0000000..bf24073 --- /dev/null +++ b/tests/modules.rs @@ -0,0 +1,108 @@ +mod mock; + +use frame_support::assert_ok; +use mock::*; +use move_core_types::{ + account_address::AccountAddress, + identifier::Identifier, + language_storage::{ModuleId, StructTag}, +}; +use pallet_move::address; + +const EMPTY_ADDR: u64 = 0x000000000CAFE_u64.to_be(); + +#[test] +/// Test getting a module. +fn get_module_correct() { + new_test_ext().execute_with(|| { + let module = include_bytes!("assets/move/build/move/bytecode_modules/Empty.mv").to_vec(); + + let res = MoveModule::publish_module(RuntimeOrigin::signed(EMPTY_ADDR), module.clone(), 0); + + assert_ok!(res); + + let address = AccountAddress::from_hex_literal("0xCAFE").unwrap(); // Alternative: let address = address::to_move_address(&ACC_ADDR); + let module_id = ModuleId::new(address, Identifier::new("Empty").unwrap()); + + let res = MoveModule::get_module(&bcs::to_bytes(&module_id).unwrap()); + + assert_eq!(res, Ok(Some(module))); + }); +} + +#[test] +/// Test getting a module that does not exist. +fn get_module_nonexistent() { + new_test_ext().execute_with(|| { + let address = AccountAddress::from_hex_literal("0xCAFE").unwrap(); + let module_id = ModuleId::new(address, Identifier::new("Empty").unwrap()); + + let res = MoveModule::get_module(&bcs::to_bytes(&module_id).unwrap()); + + assert_eq!(res, Ok(None)); + }); +} + +#[test] +/// Test getting a module providing incorrect (no module name after the address) module id. +fn get_module_invalid_input() { + new_test_ext().execute_with(|| { + let invalid_module_id = [0u8; 32]; + let errmsg = "error in get_module: unexpected end of input".as_bytes(); + let res = MoveModule::get_module(&invalid_module_id); + + assert_eq!(res, Err(errmsg.to_vec())); + }); +} + +#[test] +#[ignore = "failing - to be investigated"] +/// Test getting resource from the module. +fn get_resource_correct() { + new_test_ext().execute_with(|| { + let module = include_bytes!("assets/move/build/move/bytecode_modules/Empty.mv").to_vec(); + let resource_bytes = [0u8; 32]; // For now as we need to investigate what the resource looks like + + let res = MoveModule::publish_module(RuntimeOrigin::signed(EMPTY_ADDR), module.clone(), 0); + + assert_ok!(res); + + let address = address::to_move_address(&EMPTY_ADDR); + + let tag = StructTag { + address, + module: Identifier::new("Empty").unwrap(), + name: Identifier::new("EmptyStruct").unwrap(), + type_params: vec![], + }; + + let res = MoveModule::get_resource(&EMPTY_ADDR, &bcs::to_bytes(&tag).unwrap()); + + assert_eq!(res, Ok(Some(resource_bytes.to_vec()))); + }); +} + +#[test] +/// Test getting resource from the module. +fn get_resource_non_existent() { + new_test_ext().execute_with(|| { + let module = include_bytes!("assets/move/build/move/bytecode_modules/Empty.mv").to_vec(); + + let res = MoveModule::publish_module(RuntimeOrigin::signed(EMPTY_ADDR), module.clone(), 0); + + assert_ok!(res); + + let address = address::to_move_address(&EMPTY_ADDR); + + let tag = StructTag { + address, + module: Identifier::new("Empty").unwrap(), + name: Identifier::new("NonExistentStruct").unwrap(), + type_params: vec![], + }; + + let res = MoveModule::get_resource(&EMPTY_ADDR, &bcs::to_bytes(&tag).unwrap()); + + assert_eq!(res, Ok(None)); + }); +}