From f8c142d4dd50b3124889bb2ae3fdb20533f282e0 Mon Sep 17 00:00:00 2001 From: Tin Chung <56880684+chungquantin@users.noreply.github.com> Date: Fri, 9 Aug 2024 18:19:29 +0700 Subject: [PATCH] refactor: fungible extension test (#169) --- Cargo.lock | 3 + extension/Cargo.toml | 12 +- extension/src/lib.rs | 113 +---------------- extension/src/mock.rs | 114 ++++++++++++++++++ .../src/tests/fungibles.rs | 37 ++---- extension/src/tests/mod.rs | 110 +++++++++++++++++ runtime/devnet/src/config/mod.rs | 1 - 7 files changed, 253 insertions(+), 137 deletions(-) create mode 100644 extension/src/mock.rs rename runtime/devnet/src/config/extension.rs => extension/src/tests/fungibles.rs (63%) create mode 100644 extension/src/tests/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b59ed6de..f4efe0a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9469,12 +9469,15 @@ dependencies = [ "log", "pallet-api", "pallet-assets", + "pallet-balances", "pallet-contracts", "parity-scale-codec", "pop-primitives", "pop-runtime-common", "rand", + "scale-info", "sp-core", + "sp-io", "sp-runtime", "sp-std", ] diff --git a/extension/Cargo.toml b/extension/Cargo.toml index 35b3ecd4..3e6ef340 100644 --- a/extension/Cargo.toml +++ b/extension/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec.workspace = true log.workspace = true +scale-info.workspace = true # Local pallet-api.workspace = true @@ -25,8 +26,10 @@ pop-runtime-common.workspace = true frame-support.workspace = true frame-system.workspace = true pallet-assets.workspace = true +pallet-balances.workspace = true pallet-contracts.workspace = true sp-core.workspace = true +sp-io.workspace = true sp-runtime.workspace = true sp-std.workspace = true @@ -36,20 +39,25 @@ rand = "0.8.5" [features] default = ["std"] std = [ - "log/std", "codec/std", "frame-support/std", "frame-system/std", + "log/std", + "pallet-assets/std", + "pallet-balances/std", "pallet-contracts/std", "pop-primitives/std", - "sp-runtime/std", + "scale-info/std", "sp-core/std", + "sp-io/std", + "sp-runtime/std", "sp-std/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", "pallet-contracts/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] diff --git a/extension/src/lib.rs b/extension/src/lib.rs index d1b3dcaa..40dab961 100644 --- a/extension/src/lib.rs +++ b/extension/src/lib.rs @@ -1,6 +1,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -pub mod constants; +mod constants; +mod mock; +#[cfg(test)] +mod tests; mod v0; use codec::Encode; @@ -294,111 +297,3 @@ impl TryFrom for FuncId { Ok(id) } } - -#[cfg(test)] -mod tests { - use super::*; - - // Test ensuring `func_id()` and `ext_id()` work as expected, i.e. extracting the first two - // bytes and the last two bytes, respectively, from a 4 byte array. - #[test] - fn test_byte_extraction() { - use rand::Rng; - - // Helper functions - fn func_id(id: u32) -> u16 { - (id & 0x0000FFFF) as u16 - } - fn ext_id(id: u32) -> u16 { - (id >> 16) as u16 - } - - // Number of test iterations - let test_iterations = 1_000_000; - - // Create a random number generator - let mut rng = rand::thread_rng(); - - // Run the test for a large number of random 4-byte arrays - for _ in 0..test_iterations { - // Generate a random 4-byte array - let bytes: [u8; 4] = rng.gen(); - - // Convert the 4-byte array to a u32 value - let value = u32::from_le_bytes(bytes); - - // Extract the first two bytes (least significant 2 bytes) - let first_two_bytes = func_id(value); - - // Extract the last two bytes (most significant 2 bytes) - let last_two_bytes = ext_id(value); - - // Check if the first two bytes match the expected value - assert_eq!([bytes[0], bytes[1]], first_two_bytes.to_le_bytes()); - - // Check if the last two bytes match the expected value - assert_eq!([bytes[2], bytes[3]], last_two_bytes.to_le_bytes()); - } - } - - // Test showing all the different type of variants and its encoding. - #[test] - fn encoding_of_enum() { - #[derive(Debug, PartialEq, Encode, Decode)] - enum ComprehensiveEnum { - SimpleVariant, - DataVariant(u8), - NamedFields { w: u8 }, - NestedEnum(InnerEnum), - OptionVariant(Option), - VecVariant(Vec), - TupleVariant(u8, u8), - NestedStructVariant(NestedStruct), - NestedEnumStructVariant(NestedEnumStruct), - } - - #[derive(Debug, PartialEq, Encode, Decode)] - enum InnerEnum { - A, - B { inner_data: u8 }, - C(u8), - } - - #[derive(Debug, PartialEq, Encode, Decode)] - struct NestedStruct { - x: u8, - y: u8, - } - - #[derive(Debug, PartialEq, Encode, Decode)] - struct NestedEnumStruct { - inner_enum: InnerEnum, - } - - // Creating each possible variant for an enum. - let enum_simple = ComprehensiveEnum::SimpleVariant; - let enum_data = ComprehensiveEnum::DataVariant(42); - let enum_named = ComprehensiveEnum::NamedFields { w: 42 }; - let enum_nested = ComprehensiveEnum::NestedEnum(InnerEnum::B { inner_data: 42 }); - let enum_option = ComprehensiveEnum::OptionVariant(Some(42)); - let enum_vec = ComprehensiveEnum::VecVariant(vec![1, 2, 3, 4, 5]); - let enum_tuple = ComprehensiveEnum::TupleVariant(42, 42); - let enum_nested_struct = - ComprehensiveEnum::NestedStructVariant(NestedStruct { x: 42, y: 42 }); - let enum_nested_enum_struct = - ComprehensiveEnum::NestedEnumStructVariant(NestedEnumStruct { - inner_enum: InnerEnum::C(42), - }); - - // Encode and print each variant individually to see their encoded values. - println!("{:?} -> {:?}", enum_simple, enum_simple.encode()); - println!("{:?} -> {:?}", enum_data, enum_data.encode()); - println!("{:?} -> {:?}", enum_named, enum_named.encode()); - println!("{:?} -> {:?}", enum_nested, enum_nested.encode()); - println!("{:?} -> {:?}", enum_option, enum_option.encode()); - println!("{:?} -> {:?}", enum_vec, enum_vec.encode()); - println!("{:?} -> {:?}", enum_tuple, enum_tuple.encode()); - println!("{:?} -> {:?}", enum_nested_struct, enum_nested_struct.encode()); - println!("{:?} -> {:?}", enum_nested_enum_struct, enum_nested_enum_struct.encode()); - } -} diff --git a/extension/src/mock.rs b/extension/src/mock.rs new file mode 100644 index 00000000..6e652d7c --- /dev/null +++ b/extension/src/mock.rs @@ -0,0 +1,114 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + derive_impl, parameter_types, + traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, Everything}, +}; +use frame_system::{EnsureRoot, EnsureSigned}; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; +pub(crate) type AccountId = u64; +pub(crate) type AssetId = u32; +pub(crate) type Balance = u128; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system = 0, + Balances: pallet_balances = 1, + Assets: pallet_assets:: = 2, + Fungibles: pallet_api::fungibles = 150, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; +} + +pub(crate) type AssetsInstance = pallet_assets::Instance1; +impl pallet_assets::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type RemoveItemsLimit = ConstU32<5>; + type AssetId = AssetId; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type Extra = (); + type CallbackHandle = (); + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} +impl pallet_api::fungibles::Config for Test { + type AssetsInstance = AssetsInstance; + type WeightInfo = (); +} + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default() + .build_storage() + .expect("Frame system builds valid default genesis config"); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} diff --git a/runtime/devnet/src/config/extension.rs b/extension/src/tests/fungibles.rs similarity index 63% rename from runtime/devnet/src/config/extension.rs rename to extension/src/tests/fungibles.rs index 33417b75..6684dc6c 100644 --- a/runtime/devnet/src/config/extension.rs +++ b/extension/src/tests/fungibles.rs @@ -1,24 +1,12 @@ -#[cfg(test)] -mod tests { - use crate::{config::assets::TrustBackedAssetsInstance, Assets, Runtime, System}; - use codec::{Decode, Encode}; - use sp_runtime::{ - ArithmeticError, BuildStorage, DispatchError, ModuleError, TokenError, - MAX_MODULE_ERROR_ENCODED_SIZE, - }; +use crate::mock::{new_test_ext, Assets, AssetsInstance, Test}; +use codec::{Decode, Encode}; +use sp_runtime::{ + ArithmeticError, DispatchError, ModuleError, TokenError, MAX_MODULE_ERROR_ENCODED_SIZE, +}; - fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default() - .build_storage() - .expect("Frame system builds valid default genesis config"); - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } - - #[test] - fn encoding_decoding_dispatch_error() { - new_test_ext().execute_with(|| { +#[test] +fn encoding_decoding_dispatch_error() { + new_test_ext().execute_with(|| { let error = DispatchError::Module(ModuleError { index: 255, error: [2, 0, 0, 0], @@ -38,12 +26,12 @@ mod tests { ); // Example pallet assets Error into ModuleError. - let index = <::PalletInfo as frame_support::traits::PalletInfo>::index::< + let index = <::PalletInfo as frame_support::traits::PalletInfo>::index::< Assets, >() .expect("Every active module has an index in the runtime; qed") as u8; let mut error = - pallet_assets::Error::NotFrozen::.encode(); + pallet_assets::Error::NotFrozen::.encode(); error.resize(MAX_MODULE_ERROR_ENCODED_SIZE, 0); let error = DispatchError::Module(ModuleError { index, @@ -52,11 +40,11 @@ mod tests { }); let encoded = error.encode(); let decoded = DispatchError::decode(&mut &encoded[..]).unwrap(); - assert_eq!(encoded, vec![3, 52, 18, 0, 0, 0]); + assert_eq!(encoded, vec![3, 2, 18, 0, 0, 0]); assert_eq!( decoded, DispatchError::Module(ModuleError { - index: 52, + index: 2, error: [18, 0, 0, 0], message: None }) @@ -76,5 +64,4 @@ mod tests { assert_eq!(encoded, vec![8, 1]); assert_eq!(decoded, error); }); - } } diff --git a/extension/src/tests/mod.rs b/extension/src/tests/mod.rs new file mode 100644 index 00000000..6e46f3a3 --- /dev/null +++ b/extension/src/tests/mod.rs @@ -0,0 +1,110 @@ +#[cfg(test)] +mod fungibles; + +#[cfg(test)] +mod tests { + use codec::{Decode, Encode}; + + // Test ensuring `func_id()` and `ext_id()` work as expected, i.e. extracting the first two + // bytes and the last two bytes, respectively, from a 4 byte array. + #[test] + fn test_byte_extraction() { + use rand::Rng; + + // Helper functions + fn func_id(id: u32) -> u16 { + (id & 0x0000FFFF) as u16 + } + fn ext_id(id: u32) -> u16 { + (id >> 16) as u16 + } + + // Number of test iterations + let test_iterations = 1_000_000; + + // Create a random number generator + let mut rng = rand::thread_rng(); + + // Run the test for a large number of random 4-byte arrays + for _ in 0..test_iterations { + // Generate a random 4-byte array + let bytes: [u8; 4] = rng.gen(); + + // Convert the 4-byte array to a u32 value + let value = u32::from_le_bytes(bytes); + + // Extract the first two bytes (least significant 2 bytes) + let first_two_bytes = func_id(value); + + // Extract the last two bytes (most significant 2 bytes) + let last_two_bytes = ext_id(value); + + // Check if the first two bytes match the expected value + assert_eq!([bytes[0], bytes[1]], first_two_bytes.to_le_bytes()); + + // Check if the last two bytes match the expected value + assert_eq!([bytes[2], bytes[3]], last_two_bytes.to_le_bytes()); + } + } + + // Test showing all the different type of variants and its encoding. + #[test] + fn encoding_of_enum() { + #[derive(Debug, PartialEq, Encode, Decode)] + enum ComprehensiveEnum { + SimpleVariant, + DataVariant(u8), + NamedFields { w: u8 }, + NestedEnum(InnerEnum), + OptionVariant(Option), + VecVariant(Vec), + TupleVariant(u8, u8), + NestedStructVariant(NestedStruct), + NestedEnumStructVariant(NestedEnumStruct), + } + + #[derive(Debug, PartialEq, Encode, Decode)] + enum InnerEnum { + A, + B { inner_data: u8 }, + C(u8), + } + + #[derive(Debug, PartialEq, Encode, Decode)] + struct NestedStruct { + x: u8, + y: u8, + } + + #[derive(Debug, PartialEq, Encode, Decode)] + struct NestedEnumStruct { + inner_enum: InnerEnum, + } + + // Creating each possible variant for an enum. + let enum_simple = ComprehensiveEnum::SimpleVariant; + let enum_data = ComprehensiveEnum::DataVariant(42); + let enum_named = ComprehensiveEnum::NamedFields { w: 42 }; + let enum_nested = ComprehensiveEnum::NestedEnum(InnerEnum::B { inner_data: 42 }); + let enum_option = ComprehensiveEnum::OptionVariant(Some(42)); + let enum_vec = ComprehensiveEnum::VecVariant(vec![1, 2, 3, 4, 5]); + let enum_tuple = ComprehensiveEnum::TupleVariant(42, 42); + let enum_nested_struct = + ComprehensiveEnum::NestedStructVariant(NestedStruct { x: 42, y: 42 }); + let enum_nested_enum_struct = + ComprehensiveEnum::NestedEnumStructVariant(NestedEnumStruct { + inner_enum: InnerEnum::C(42), + }); + + // Encode and print each variant individually to see their encoded values. + println!("{:?} -> {:?}", enum_simple, enum_simple.encode()); + println!("{:?} -> {:?}", enum_data, enum_data.encode()); + println!("{:?} -> {:?}", enum_named, enum_named.encode()); + println!("{:?} -> {:?}", enum_nested, enum_nested.encode()); + println!("{:?} -> {:?}", enum_option, enum_option.encode()); + println!("{:?} -> {:?}", enum_vec, enum_vec.encode()); + println!("{:?} -> {:?}", enum_tuple, enum_tuple.encode()); + println!("{:?} -> {:?}", enum_nested_struct, enum_nested_struct.encode()); + println!("{:?} -> {:?}", enum_nested_enum_struct, enum_nested_enum_struct.encode()); + } +} diff --git a/runtime/devnet/src/config/mod.rs b/runtime/devnet/src/config/mod.rs index ed67f1f7..f62ffa76 100644 --- a/runtime/devnet/src/config/mod.rs +++ b/runtime/devnet/src/config/mod.rs @@ -1,7 +1,6 @@ pub(crate) mod api; pub mod assets; mod contracts; -mod extension; mod proxy; // Public due to integration tests crate. pub mod xcm;