diff --git a/Cargo.lock b/Cargo.lock index fc1438cf..74b164d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8045,6 +8045,7 @@ name = "pontem-runtime" version = "3.0.0" dependencies = [ "assets", + "bcs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "constants", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -8064,6 +8065,7 @@ dependencies = [ "hex-literal", "kusama-runtime", "module-currencies", + "move-core-types", "mvm", "nimbus-primitives", "orml-tokens", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 0fc82e8f..19eaa360 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -104,6 +104,8 @@ sp-io = { default-features = false, git = 'https://github.com/paritytech/substra assets = { path = '../assets', default-features = false } env_logger = "0.9.0" test-log = "0.2.8" +move-core-types = { git = "https://github.com/pontem-network/sp-move-vm.git", rev = "c8ac6f91c7ec95e62afa3ee9ef9884eec113511c", default-features = false } +bcs = { package = "bcs", version = "0.1" } [dependencies.move-vm] package = "mvm" diff --git a/runtime/src/tests/assets/build_assets.sh b/runtime/src/tests/assets/build_assets.sh index c357e844..03c3961c 100755 --- a/runtime/src/tests/assets/build_assets.sh +++ b/runtime/src/tests/assets/build_assets.sh @@ -17,6 +17,8 @@ popd pushd ./user dove clean dove build -b +dove tx "store_system_block()" +dove tx "store_system_timestamp()" dove tx "transfer<0x1::NOX::NOX>(Alice, 500000000000)" -o=transfer_pont.mvt dove tx "transfer<0x1::KSM::KSM>(Alice, 500000000000)" -o=transfer_ksm.mvt dove tx "deposit_bank<0x1::NOX::NOX>(500000000000)" -o=deposit_bank_pont.mvt diff --git a/runtime/src/tests/assets/user/scripts/SysBlock.move b/runtime/src/tests/assets/user/scripts/SysBlock.move new file mode 100644 index 00000000..4eadb4a0 --- /dev/null +++ b/runtime/src/tests/assets/user/scripts/SysBlock.move @@ -0,0 +1,9 @@ +script { + use PontemFramework::PontBlock; + use RuntimeTests::Store; + + fun store_system_block(account: signer) { + Store::store_u64(&account, PontBlock::get_current_block_height()); + } +} + diff --git a/runtime/src/tests/assets/user/scripts/SysTimestamp.move b/runtime/src/tests/assets/user/scripts/SysTimestamp.move new file mode 100644 index 00000000..efc22094 --- /dev/null +++ b/runtime/src/tests/assets/user/scripts/SysTimestamp.move @@ -0,0 +1,8 @@ +script { + use PontemFramework::PontTimestamp; + use RuntimeTests::Store; + + fun store_system_timestamp(account: signer) { + Store::store_u64(&account, PontTimestamp::now_microseconds()); + } +} diff --git a/runtime/src/tests/assets/user/sources/Store.move b/runtime/src/tests/assets/user/sources/Store.move new file mode 100644 index 00000000..fc4326f2 --- /dev/null +++ b/runtime/src/tests/assets/user/sources/Store.move @@ -0,0 +1,29 @@ +module RuntimeTests::Store { + struct U64 has key { val: u64 } + + struct U128 has key { val: u128 } + + struct Address has key { val: address } + + struct VectorU8 has key { val: vector } + + public fun store_u64(account: &signer, val: u64) { + let foo = U64 { val: val }; + move_to(account, foo); + } + + public fun store_u128(account: &signer, val: u128) { + let foo = U128 { val: val }; + move_to(account, foo); + } + + public fun store_address(account: &signer, val: address) { + let addr = Address { val: val }; + move_to
(account, addr); + } + + public fun store_vector_u8(account: &signer, val: vector) { + let vec = VectorU8 { val: val }; + move_to(account, vec); + } +} diff --git a/runtime/src/tests/ensure_root.rs b/runtime/src/tests/ensure_root.rs new file mode 100644 index 00000000..77634ab1 --- /dev/null +++ b/runtime/src/tests/ensure_root.rs @@ -0,0 +1,81 @@ +use crate::tests::mock::{ + RuntimeBuilder, Accounts, Origin, TransactionPause, CurrencyId, Currencies, ParachainStaking, + Perbill, +}; +use frame_support::{assert_ok, assert_err, error::BadOrigin}; + +#[test] +fn ensure_root_in_transaction_pause() { + RuntimeBuilder::new().build().execute_with(|| { + assert_ok!(TransactionPause::pause_transaction( + Origin::root(), + b"Balances".to_vec(), + b"transfer".to_vec() + )); + assert_err!( + TransactionPause::pause_transaction( + Origin::signed(Accounts::BOB.account()), + b"Balances".to_vec(), + b"transfer".to_vec() + ), + BadOrigin + ); + }) +} + +#[test] +fn ensure_root_in_update_balance() { + RuntimeBuilder::new().build().execute_with(|| { + assert_err!( + Currencies::update_balance( + Origin::signed(Accounts::ALICE.account()), + Accounts::ALICE.account().into(), + CurrencyId::NATIVE, + 100 + ), + BadOrigin + ); + assert_ok!(Currencies::update_balance( + Origin::root(), + Accounts::ALICE.account().into(), + CurrencyId::NATIVE, + 100 + )); + }); +} + +#[test] +fn ensure_root_in_parachain_staking() { + RuntimeBuilder::new().build().execute_with(|| { + assert_err!( + ParachainStaking::set_blocks_per_round( + Origin::signed(Accounts::ALICE.account()), + 20u32 + ), + BadOrigin + ); + assert_ok!(ParachainStaking::set_blocks_per_round( + Origin::root(), + 42u32 + )); + assert_err!( + ParachainStaking::set_collator_commission( + Origin::signed(Accounts::ALICE.account()), + Perbill::from_percent(5) + ), + BadOrigin + ); + assert_ok!(ParachainStaking::set_collator_commission( + Origin::root(), + Perbill::from_percent(5) + )); + assert_err!( + ParachainStaking::set_total_selected( + Origin::signed(Accounts::ALICE.account()), + 42u32 + ), + BadOrigin + ); + assert_ok!(ParachainStaking::set_total_selected(Origin::root(), 42u32)); + }) +} diff --git a/runtime/src/tests/mock.rs b/runtime/src/tests/mock.rs index c0bc9203..43e36bf3 100644 --- a/runtime/src/tests/mock.rs +++ b/runtime/src/tests/mock.rs @@ -1,8 +1,10 @@ pub use crate::*; use frame_support::sp_io::TestExternalities; use frame_support::traits::GenesisBuild; -use sp_core::crypto::Ss58Codec; +use move_core_types::account_address::AccountAddress; +use sp_core::{Encode, crypto::Ss58Codec}; use frame_support::traits::Hooks; +use sp_core::sr25519::Public; use std::include_bytes; use move_vm::genesis::GenesisConfig; @@ -21,6 +23,9 @@ pub fn build_vm_config() -> (ModuleName, FunctionName, FunctionArgs) { ) } +/// Timestamp multiplier. +pub const TIME_BLOCK_MULTIPLIER: u64 = 12000; + /// User accounts. pub enum Accounts { ALICE, @@ -28,19 +33,28 @@ pub enum Accounts { } impl Accounts { - /// Convert account to AccountId. - pub fn account(&self) -> AccountId { + fn ss58(&self) -> &'static str { match self { - Accounts::ALICE => { - AccountId::from_ss58check("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY") - .unwrap() - } - Accounts::BOB => { - AccountId::from_ss58check("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty") - .unwrap() - } + Accounts::ALICE => "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + Accounts::BOB => "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", } } + + /// Convert account to AccountId. + pub fn account(&self) -> AccountId { + AccountId::from_ss58check(self.ss58()).unwrap() + } + + pub fn public_key(&self) -> Public { + Public::from_ss58check_with_version(self.ss58()).unwrap().0 + } + + pub fn addr(&self) -> AccountAddress { + let key = self.public_key().encode(); + let mut arr = [0; AccountAddress::LENGTH]; + arr.copy_from_slice(&key); + AccountAddress::new(arr) + } } impl Into<[u8; 32]> for Accounts { @@ -61,7 +75,7 @@ pub fn run_to_block(till: u32) { Scheduler::on_finalize(System::block_number()); Balances::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); - Timestamp::set_timestamp(System::block_number() as u64 * 12000); + Timestamp::set_timestamp(System::block_number() as u64 * TIME_BLOCK_MULTIPLIER); Scheduler::on_initialize(System::block_number()); ParachainStaking::on_initialize(System::block_number()); TransactionPause::on_initialize(System::block_number()); diff --git a/runtime/src/tests/mod.rs b/runtime/src/tests/mod.rs index e354a452..71c2ccaa 100644 --- a/runtime/src/tests/mod.rs +++ b/runtime/src/tests/mod.rs @@ -7,3 +7,9 @@ pub mod vesting; // Parachain tests. pub mod parachain; + +// MVM scripts tests +pub mod scripts; + +// Root access tests +pub mod ensure_root; diff --git a/runtime/src/tests/scripts.rs b/runtime/src/tests/scripts.rs new file mode 100644 index 00000000..aeb3ea1e --- /dev/null +++ b/runtime/src/tests/scripts.rs @@ -0,0 +1,113 @@ +use crate::tests::mock::{RuntimeBuilder, Accounts, Mvm, Origin, run_to_block, TIME_BLOCK_MULTIPLIER}; +use frame_support::assert_ok; +use move_core_types::{ + language_storage::StructTag, identifier::Identifier, account_address::AccountAddress, + resolver::ResourceResolver, +}; +use sp_mvm::storage::MoveVmStorage; +use move_vm::io::state::State; + +pub mod modules { + use assets::Asset; + pub static STORE: Asset = Asset::new( + "Store", + "src/tests/assets/user/build/assets/bytecode_modules/Store.mv", + ); +} + +pub mod transactions { + use assets::Asset; + pub static STORE_SYSTEM_BLOCK: Asset = Asset::new( + "store_system_block", + "src/tests/assets/user/build/assets/transaction/store_system_block.mvt", + ); + pub static STORE_SYSTEM_TIMESTAMP: Asset = Asset::new( + "store_system_timestamp", + "src/tests/assets/user/build/assets/transaction/store_system_timestamp.mvt", + ); +} + +const GAS_LIMIT: u64 = 1_000_000; + +/// Check stored value (u64) inside storage. +fn check_stored_value(expected: u64) { + #[derive(serde::Deserialize, Debug, PartialEq)] + struct StoreU64 { + pub val: u64, + } + + let expected = StoreU64 { val: expected }; + + let tag = StructTag { + address: Accounts::BOB.addr(), + module: Identifier::new(modules::STORE.name()).unwrap(), + name: Identifier::new("U64").unwrap(), + type_params: vec![], + }; + + check_storage_res(Accounts::BOB.addr(), tag, expected); +} + +/// Check resource value inside storage. +pub fn check_storage_res(owner: AccountAddress, ty: StructTag, expected: T) +where + T: for<'de> serde::Deserialize<'de>, + T: std::cmp::PartialEq + std::fmt::Debug, +{ + let storage = Mvm::move_vm_storage(); + let state = State::new(storage); + let blob = state + .get_resource(&owner, &ty) + .expect("VM state read storage (resource)") + .expect(&format!("Resource '{}' should exist", ty)); + + let tt_str = format!("{}", ty); + println!("checking stored resource '{}'", tt_str); + let stored: T = + bcs::from_bytes(&blob).expect(&format!("Resource '{}' should exists", tt_str)); + assert_eq!(expected, stored); +} + +#[test] +/// Execute storing of block height inside module by calling script. +fn execute_store_block() { + RuntimeBuilder::new().build().execute_with(|| { + // Publish STORE module. + assert_ok!(Mvm::publish_module( + Origin::signed(Accounts::BOB.account()), + modules::STORE.bytes().to_vec(), + GAS_LIMIT + )); + + const EXPECTED: u32 = 3; + run_to_block(EXPECTED); + assert_ok!(Mvm::execute( + Origin::signed(Accounts::BOB.account()), + transactions::STORE_SYSTEM_BLOCK.bytes().to_vec(), + GAS_LIMIT + )); + check_stored_value(EXPECTED.into()); + }); +} + +#[test] +/// Execute storing of timestamp inside module by calling script. +fn execute_store_time() { + RuntimeBuilder::new().build().execute_with(|| { + // Publish STORE module. + assert_ok!(Mvm::publish_module( + Origin::signed(Accounts::BOB.account()), + modules::STORE.bytes().to_vec(), + GAS_LIMIT + )); + + const EXPECTED: u32 = 3; + run_to_block(EXPECTED); + assert_ok!(Mvm::execute( + Origin::signed(Accounts::BOB.account()), + transactions::STORE_SYSTEM_TIMESTAMP.bytes().to_vec(), + GAS_LIMIT + )); + check_stored_value(EXPECTED as u64 * TIME_BLOCK_MULTIPLIER); + }); +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f96fe958..85d09db2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] channel = "nightly-2021-11-07" -profile = "complete" +profile = "default" targets = [ "wasm32-unknown-unknown"]