diff --git a/Cargo.toml b/Cargo.toml index 81aff60..1e4b065 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,14 @@ description = "MoveVM support pallet" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +anyhow = { version = "1.0", default-features = false } 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" } frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0" } +serde = { version = "1.0", default-features = false, features = ["derive"] } sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v1.0.0' } # MoveVM dependencies diff --git a/src/lib.rs b/src/lib.rs index 0d493b4..0f51dc9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ mod benchmarking; mod storage; +pub mod transaction; + pub mod weights; pub use weights::*; @@ -30,6 +32,7 @@ pub mod pallet { use move_vm_backend::Mvm; use move_vm_types::gas::UnmeteredGasMeter; use sp_std::{default::Default, vec::Vec}; + use transaction::Transaction; use super::*; use crate::storage::MoveVmStorage; @@ -80,13 +83,25 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::execute())] pub fn execute( origin: OriginFor, - _bytecode: Vec, + transaction_bc: Vec, _gas_limit: u64, ) -> DispatchResult { // Allow only signed calls. let who = ensure_signed(origin)?; - // TODO: Execute bytecode + let storage = Self::move_vm_storage(); + let vm = Mvm::new(storage).map_err(|_err| Error::::ExecuteFailed)?; + + let transaction = Transaction::try_from(transaction_bc.as_slice()) + .map_err(|_| Error::::ExecuteFailed)?; + + vm.execute_script( + transaction.script_bc.as_slice(), + transaction.type_args, + transaction.args.iter().map(|x| x.as_slice()).collect(), + &mut UnmeteredGasMeter, // TODO(asmie): gas handling + ) + .map_err(|_err| Error::::ExecuteFailed)?; // Emit an event. Self::deposit_event(Event::ExecuteCalled { who }); diff --git a/src/transaction.rs b/src/transaction.rs new file mode 100644 index 0000000..66d40b5 --- /dev/null +++ b/src/transaction.rs @@ -0,0 +1,26 @@ +use core::convert::TryFrom; + +//TODO: either leave it as is when moving to move-vm-backend (then remove this comment) or +// change it to crate error type +use anyhow::Error; +use frame_support::dispatch::Vec; +use move_core_types::language_storage::TypeTag; +use serde::{Deserialize, Serialize}; + +/// Transaction representation used in execute call. +#[derive(Serialize, Deserialize, Debug)] +pub struct Transaction { + /// Script bytecode. + pub script_bc: Vec, + /// Script args. + pub args: Vec>, + /// Script type arguments. + pub type_args: Vec, +} + +impl TryFrom<&[u8]> for Transaction { + type Error = Error; + fn try_from(blob: &[u8]) -> Result { + bcs::from_bytes(blob).map_err(Error::msg) + } +} diff --git a/tests/assets/move/build/move/bytecode_modules/EmptyBob.mv b/tests/assets/move/build/move/bytecode_modules/EmptyBob.mv new file mode 100644 index 0000000..12e1598 Binary files /dev/null and b/tests/assets/move/build/move/bytecode_modules/EmptyBob.mv differ diff --git a/tests/assets/move/build/move/bytecode_scripts/empty_loop.mv b/tests/assets/move/build/move/bytecode_scripts/empty_loop.mv new file mode 100644 index 0000000..962cbcf Binary files /dev/null and b/tests/assets/move/build/move/bytecode_scripts/empty_loop.mv differ diff --git a/tests/assets/move/build/move/bytecode_scripts/empty_loop_param.mv b/tests/assets/move/build/move/bytecode_scripts/empty_loop_param.mv new file mode 100644 index 0000000..b443345 Binary files /dev/null and b/tests/assets/move/build/move/bytecode_scripts/empty_loop_param.mv differ diff --git a/tests/assets/move/build/move/bytecode_scripts/empty_scr.mv b/tests/assets/move/build/move/bytecode_scripts/empty_scr.mv new file mode 100644 index 0000000..a76c05d Binary files /dev/null and b/tests/assets/move/build/move/bytecode_scripts/empty_scr.mv differ diff --git a/tests/assets/move/build/move/bytecode_scripts/generic_1.mv b/tests/assets/move/build/move/bytecode_scripts/generic_1.mv new file mode 100644 index 0000000..3c2dc5b Binary files /dev/null and b/tests/assets/move/build/move/bytecode_scripts/generic_1.mv differ diff --git a/tests/assets/move/scripts/Generics.move b/tests/assets/move/scripts/Generics.move new file mode 100644 index 0000000..6db2e73 --- /dev/null +++ b/tests/assets/move/scripts/Generics.move @@ -0,0 +1,5 @@ +script { + fun generic_1(x: T) { + let _y = x; + } +} \ No newline at end of file diff --git a/tests/assets/move/scripts/Loops.move b/tests/assets/move/scripts/Loops.move new file mode 100644 index 0000000..8418f0d --- /dev/null +++ b/tests/assets/move/scripts/Loops.move @@ -0,0 +1,17 @@ +script { + fun empty_loop() { + let iterations: u64 = 10; + + while (iterations > 0) { + iterations = iterations - 1; + } + } +} + +script { + fun empty_loop_param(iterations: u64) { + while (iterations > 0) { + iterations = iterations - 1; + } + } +} diff --git a/tests/execute.rs b/tests/execute.rs index 3148f0b..fff1e16 100644 --- a/tests/execute.rs +++ b/tests/execute.rs @@ -1,12 +1,98 @@ mod mock; +use frame_support::assert_ok; use mock::*; +use move_core_types::language_storage::TypeTag; +use pallet_move::transaction::Transaction; #[test] -#[ignore = "to be implemented"] -/// Test execution of a script with correct parameters. -fn execute_script_correct() { +/// Test execution of a script. +fn execute_script_empty() { new_test_ext().execute_with(|| { - assert_eq!(1, 0); + let module = + include_bytes!("assets/move/build/move/bytecode_scripts/empty_scr.mv").to_vec(); + + let type_args: Vec = vec![]; + let params: Vec<&[u8]> = vec![]; + + let transaction = Transaction { + script_bc: module.clone(), + type_args, + args: params.iter().map(|x| x.to_vec()).collect(), + }; + + let transaction_bc = bcs::to_bytes(&transaction).unwrap(); + + let res = MoveModule::execute(RuntimeOrigin::signed(0xFECA000000000000), transaction_bc, 0); + + assert_ok!(res); + + let module = + include_bytes!("assets/move/build/move/bytecode_scripts/empty_loop.mv").to_vec(); + + let type_args: Vec = vec![]; + let params: Vec<&[u8]> = vec![]; + + let transaction = Transaction { + script_bc: module.clone(), + type_args, + args: params.iter().map(|x| x.to_vec()).collect(), + }; + + let transaction_bc = bcs::to_bytes(&transaction).unwrap(); + + let res = MoveModule::execute(RuntimeOrigin::signed(0xFECA000000000000), transaction_bc, 0); + + assert_ok!(res); + }); +} + +#[test] +/// Test execution of a script with parametrized function. +fn execute_script_params() { + new_test_ext().execute_with(|| { + let module = + include_bytes!("assets/move/build/move/bytecode_scripts/empty_loop_param.mv").to_vec(); + + let iter_count = bcs::to_bytes(&10u64).unwrap(); + let type_args: Vec = vec![]; + let params: Vec<&[u8]> = vec![&iter_count]; + + let transaction = Transaction { + script_bc: module.clone(), + type_args, + args: params.iter().map(|x| x.to_vec()).collect(), + }; + + let transaction_bc = bcs::to_bytes(&transaction).unwrap(); + + let res = MoveModule::execute(RuntimeOrigin::signed(0xFECA000000000000), transaction_bc, 0); + + assert_ok!(res); + }); +} + +#[test] +/// Test execution of a script with generic function. +fn execute_script_generic() { + new_test_ext().execute_with(|| { + let module = + include_bytes!("assets/move/build/move/bytecode_scripts/generic_1.mv").to_vec(); + + let param = bcs::to_bytes(&100u64).unwrap(); + let type_args: Vec = vec![TypeTag::U64]; + let params: Vec<&[u8]> = vec![¶m]; + + let transaction = Transaction { + script_bc: module.clone(), + type_args, + args: params.iter().map(|x| x.to_vec()).collect(), + }; + + let transaction_bc = bcs::to_bytes(&transaction).unwrap(); + + let res = MoveModule::execute(RuntimeOrigin::signed(0xFECA000000000000), transaction_bc, 0); + + assert_ok!(res); }); }