Skip to content

Commit

Permalink
feat: handling get_module*/get_resource calls
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
asmie authored Oct 18, 2023
1 parent f4f67c7 commit b20c9d2
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
44 changes: 44 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -160,4 +166,42 @@ pub mod pallet {
{
type VmStorage = VMStorage<T>;
}

impl<T: Config> Pallet<T> {
// Internal helper for creating new MoveVM instance with StorageAdapter.
fn move_vm() -> Result<Mvm<crate::storage::StorageAdapter<VMStorage<T>>>, Vec<u8>> {
let storage = Self::move_vm_storage();

Mvm::new(storage).map_err::<Vec<u8>, _>(|err| {
format!("error while creating the vm {:?}", err).into()
})
}

pub fn get_module_abi(module_id: &[u8]) -> Result<Option<Vec<u8>>, Vec<u8>> {
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<Option<Vec<u8>>, Vec<u8>> {
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<Option<Vec<u8>>, Vec<u8>> {
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())
}
}
}
2 changes: 2 additions & 0 deletions tests/assets/move/sources/Empty.move
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
module TestAccount::Empty {
struct EmptyStruct {
}
}
108 changes: 108 additions & 0 deletions tests/modules.rs
Original file line number Diff line number Diff line change
@@ -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));
});
}

0 comments on commit b20c9d2

Please sign in to comment.