diff --git a/Cargo.lock b/Cargo.lock index f6c3a59..6c27fac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6683,6 +6683,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", + "pallet-collective", "pallet-evm", "pallet-session", "pallet-timestamp", @@ -6697,6 +6698,7 @@ dependencies = [ "sp-std", "sp-version", "stability-test-runtime-client", + "stbl-core-primitives", ] [[package]] diff --git a/pallets/upgrade-runtime-proposal/src/lib.rs b/pallets/upgrade-runtime-proposal/src/lib.rs index a953de2..667ba69 100644 --- a/pallets/upgrade-runtime-proposal/src/lib.rs +++ b/pallets/upgrade-runtime-proposal/src/lib.rs @@ -11,6 +11,7 @@ mod mock; mod tests; use frame_support::dispatch::UnfilteredDispatchable; +use frame_support::sp_runtime::traits::Hash; use frame_support::traits::EnsureOrigin; use frame_system::pallet_prelude::BlockNumberFor; use frame_system::pallet_prelude::OriginFor; @@ -42,6 +43,10 @@ pub mod pallet { #[pallet::getter(fn application_block_number)] pub type ApplicationBlockNumber = StorageValue<_, T::BlockNumber, OptionQuery>; + #[pallet::storage] + #[pallet::getter(fn current_code_hash)] + pub type CurrentCodeHash = StorageValue<_, T::Hash, OptionQuery>; + #[pallet::error] pub enum Error { /// The address received is invalid @@ -154,6 +159,8 @@ pub mod pallet { if result.is_err() { log::error!("Failed to upgrade runtime"); } else { + let hash = Pallet::::hash_of_proposed_code().unwrap(); + Pallet::::set_current_code_hash(hash); log::info!("Runtime upgraded"); } @@ -173,6 +180,18 @@ pub mod pallet { >::get() } + pub fn get_current_code_hash() -> Option { + >::get() + } + + pub fn hash_of_proposed_code() -> Option { + >::get().map(|code| T::Hashing::hash(&code)) + } + + pub fn set_current_code_hash(hash: T::Hash) -> () { + >::put(hash) + } + pub fn set_application_block_number(block_number: T::BlockNumber) { >::put(block_number); } diff --git a/pallets/upgrade-runtime-proposal/src/mock.rs b/pallets/upgrade-runtime-proposal/src/mock.rs index ef04a02..51648ae 100644 --- a/pallets/upgrade-runtime-proposal/src/mock.rs +++ b/pallets/upgrade-runtime-proposal/src/mock.rs @@ -5,12 +5,12 @@ use frame_support::parameter_types; use frame_support::traits::{ConstU32, ConstU64, Contains}; use frame_system::EnsureRoot; use sp_core::H256; +use sp_runtime::generic; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; use sp_version::RuntimeVersion; -use sp_runtime::generic; type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; diff --git a/pallets/upgrade-runtime-proposal/src/tests.rs b/pallets/upgrade-runtime-proposal/src/tests.rs index 2d6eba0..80eab59 100644 --- a/pallets/upgrade-runtime-proposal/src/tests.rs +++ b/pallets/upgrade-runtime-proposal/src/tests.rs @@ -1,12 +1,10 @@ use crate::mock::UpgradeRuntimeProposal; use super::*; +use frame_support::traits::Hooks; use frame_support::{assert_noop, assert_ok, dispatch::DispatchError}; -use mock::{ - new_test_ext, Test, assert_runtime_updated_digest -}; +use mock::{assert_runtime_updated_digest, new_test_ext, Test}; use sp_core::keccak_256; -use frame_support::traits::Hooks; #[test] fn test_setup_works() { @@ -103,7 +101,10 @@ fn test_set_block_application() { frame_system::RawOrigin::Root.into(), 1 )); - assert_eq!(UpgradeRuntimeProposal::get_application_block_number().unwrap(), 1); + assert_eq!( + UpgradeRuntimeProposal::get_application_block_number().unwrap(), + 1 + ); }); } @@ -197,25 +198,37 @@ fn test_fails_reject_proposed_code_if_no_proposed_code() { #[test] fn test_scheduled_update_runtime() { - let executor = stability_test_runtime_client::new_native_or_wasm_executor(); - let mut ext = new_test_ext(); - ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); - - ext.execute_with(|| { - assert_ok!(UpgradeRuntimeProposal::propose_code( - frame_system::RawOrigin::Root.into(), - stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec() - )); - assert_ok!(UpgradeRuntimeProposal::set_block_application( - frame_system::RawOrigin::Root.into(), - 1 - )); - assert_eq!(UpgradeRuntimeProposal::get_application_block_number().unwrap(), 1); - - UpgradeRuntimeProposal::on_initialize(1); - - assert_runtime_updated_digest(1); - assert!(UpgradeRuntimeProposal::get_proposed_code().is_none()); - assert!(UpgradeRuntimeProposal::get_application_block_number().is_none()); - }); -} \ No newline at end of file + let executor = stability_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = new_test_ext(); + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + + ext.execute_with(|| { + assert_ok!(UpgradeRuntimeProposal::propose_code( + frame_system::RawOrigin::Root.into(), + stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec() + )); + + let proposed_code_hash = UpgradeRuntimeProposal::hash_of_proposed_code().unwrap(); + + assert_ok!(UpgradeRuntimeProposal::set_block_application( + frame_system::RawOrigin::Root.into(), + 1 + )); + assert_eq!( + UpgradeRuntimeProposal::get_application_block_number().unwrap(), + 1 + ); + + UpgradeRuntimeProposal::on_initialize(1); + + assert_runtime_updated_digest(1); + + assert!(UpgradeRuntimeProposal::hash_of_proposed_code().is_none()); + assert_eq!( + UpgradeRuntimeProposal::get_current_code_hash().unwrap(), + proposed_code_hash + ); + assert!(UpgradeRuntimeProposal::get_proposed_code().is_none()); + assert!(UpgradeRuntimeProposal::get_application_block_number().is_none()); + }); +} diff --git a/precompiles/upgrade-runtime-controller/Cargo.toml b/precompiles/upgrade-runtime-controller/Cargo.toml index 66079f6..460af83 100644 --- a/precompiles/upgrade-runtime-controller/Cargo.toml +++ b/precompiles/upgrade-runtime-controller/Cargo.toml @@ -22,6 +22,7 @@ pallet-evm = { workspace = true, features = [ "forbid-evm-reentrancy" ] } # Stability pallet-upgrade-runtime-proposal = { workspace = true } precompile-utils = { workspace = true } +pallet-collective = { workspace = true } [dev-dependencies] pallet-balances = { workspace = true } @@ -32,6 +33,7 @@ scale-info = { version = "2.0", default-features = false, features = [ "derive" sha3 = "0.10" sp-io = { workspace = true } stability-test-runtime-client = { workspace = true } +stbl-core-primitives = { workspace = true, features = ["std"] } [features] default = [ "std" ] diff --git a/precompiles/upgrade-runtime-controller/UpgradeRuntimeController.sol b/precompiles/upgrade-runtime-controller/UpgradeRuntimeController.sol index d90756c..7bc993f 100644 --- a/precompiles/upgrade-runtime-controller/UpgradeRuntimeController.sol +++ b/precompiles/upgrade-runtime-controller/UpgradeRuntimeController.sol @@ -2,6 +2,14 @@ pragma solidity >=0.8.3; interface UpgradeRuntimeController { + event MemberAdded(address member); + event MemberRemoved(address member); + function setApplicationBlock(uint32 block) external; // onlyOwner function rejectProposedCode() external; // onlyOwner + function getTechnicalCommitteeMembers() external view returns (address[] memory); + function addMemberToTechnicalCommittee(address member) external; // onlyOwner + function removeMemberFromTechnicalCommittee(address member) external; // onlyOwner + function getHashOfProposedCode() external view returns (bytes32); + function getHashOfCurrentCode() external view returns (bytes32); } \ No newline at end of file diff --git a/precompiles/upgrade-runtime-controller/src/lib.rs b/precompiles/upgrade-runtime-controller/src/lib.rs index 6494c2b..31d20b6 100644 --- a/precompiles/upgrade-runtime-controller/src/lib.rs +++ b/precompiles/upgrade-runtime-controller/src/lib.rs @@ -30,6 +30,8 @@ use frame_support::dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}; use frame_support::parameter_types; use frame_support::storage::types::{StorageValue, ValueQuery}; +use frame_support::inherent::Vec; +use frame_support::traits::ChangeMembers; use frame_support::traits::StorageInstance; use precompile_utils::prelude::*; @@ -50,6 +52,8 @@ pub const SELECTOR_LOG_TRANSFER_OWNER: [u8; 32] = pub const SELECTOR_SETTED__APPLICATION_BLOCK: [u8; 32] = keccak256!("SettedApplicationBlock(uint256)"); pub const SELECTOR_CODE_PROPOSED_REJECTED: [u8; 32] = keccak256!("CodeProposedRejected()"); +pub const SELECTOR_MEMBER_ADDED: [u8; 32] = keccak256!("MemberAdded(address)"); +pub const SELECTOR_MEMBER_REMOVED: [u8; 32] = keccak256!("MemberRemoved(address)"); /// Storage prefix for owner. pub struct OwnerPrefix; @@ -83,13 +87,18 @@ pub struct UpgradeRuntimeControllerPrecompile + impl UpgradeRuntimeControllerPrecompile where DefaultOwner: Get + 'static, - Runtime: - pallet_upgrade_runtime_proposal::Config + pallet_timestamp::Config + pallet_evm::Config, + Runtime: pallet_upgrade_runtime_proposal::Config + + pallet_collective::Config + + pallet_timestamp::Config + + pallet_evm::Config, Runtime::RuntimeCall: From>, ::RuntimeOrigin: From>, Runtime::RuntimeCall: Dispatchable + GetDispatchInfo, ::Moment: Into, ::BlockNumber: From, + ::Hash: Into, + ::AccountId: From, + ::AccountId: Into, { #[precompile::public("owner()")] #[precompile::view] @@ -130,7 +139,7 @@ where handle.context().address, SELECTOR_LOG_TRANSFER_OWNER, Into::::into(owner), - solidity::encode_event_data(Into::::into(target_new_owner)) + solidity::encode_event_data(Into::::into(target_new_owner)), ) .record(handle)?; @@ -160,7 +169,7 @@ where log1( handle.context().address, SELECTOR_LOG_NEW_OWNER, - solidity::encode_event_data(Into::::into(target_new_owner)) + solidity::encode_event_data(Into::::into(target_new_owner)), ) .record(handle)?; @@ -221,4 +230,145 @@ where Ok(()) } + + #[precompile::public("addMemberToTechnicalCommittee(address)")] + fn add_member_to_technical_committee( + handle: &mut impl PrecompileHandle, + member: Address, + ) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + let msg_sender = handle.context().caller; + let owner = OwnerStorage::::get(); + + if msg_sender != owner { + return Err(revert("sender is not owner")); + } + + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + let member_id: H160 = member.into(); + + let old_members = + pallet_collective::Pallet::::members(); + + if old_members.contains(&member_id.into()) { + return Err(revert("already a member")); + } + + let mut new_members = old_members + .iter() + .cloned() + .chain(Some(member_id.into())) + .collect::>(); + + new_members.sort(); + + pallet_collective::Pallet::::set_members_sorted( + &new_members, + &old_members, + ); + + handle.record_log_costs_manual(1, 32)?; + log1( + handle.context().address, + SELECTOR_MEMBER_ADDED, + solidity::encode_event_data(Into::
::into(member_id)), + ) + .record(handle)?; + + Ok(()) + } + + #[precompile::public("removeMemberFromTechnicalCommittee(address)")] + fn remove_member_to_technical_committee( + handle: &mut impl PrecompileHandle, + member: Address, + ) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + let msg_sender = handle.context().caller; + let owner = OwnerStorage::::get(); + + if msg_sender != owner { + return Err(revert("sender is not owner")); + } + + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + let member_id: H160 = member.into(); + let member_account = ::AccountId::from(member_id); + + let old_members = + pallet_collective::Pallet::::members(); + + if !old_members.contains(&member_account) { + return Err(revert("not a member")); + } + + let mut new_members = old_members + .iter() + .cloned() + .filter(|m| *m != member_account) + .collect::>(); + + new_members.sort(); + + pallet_collective::Pallet::::set_members_sorted( + &new_members, + &old_members, + ); + + handle.record_log_costs_manual(1, 32)?; + log1( + handle.context().address, + SELECTOR_MEMBER_REMOVED, + solidity::encode_event_data(Into::
::into(member_id)), + ) + .record(handle)?; + + Ok(()) + } + + #[precompile::public("getTechnicalCommitteeMembers()")] + #[precompile::view] + fn get_technical_committee_members( + handle: &mut impl PrecompileHandle, + ) -> EvmResult> { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + let members = pallet_collective::Pallet::::members(); + + Ok(members + .iter() + .map(|m| Into::::into(m.clone())) + .map(|m| Into::
::into(m)) + .collect()) + } + + #[precompile::public("getHashOfProposedCode()")] + #[precompile::view] + fn get_hash_of_proposed_code(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + let hash = pallet_upgrade_runtime_proposal::Pallet::::hash_of_proposed_code(); + + match hash { + Some(hash) => Ok(hash.into()), + None => Ok(H256::default()), + } + } + + #[precompile::public("getHashOfCurrentCode()")] + #[precompile::view] + fn get_hash_of_current_code(handle: &mut impl PrecompileHandle) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + + let hash = pallet_upgrade_runtime_proposal::Pallet::::get_current_code_hash(); + + match hash { + Some(hash) => Ok(hash.into()), + None => Ok(H256::default()), + } + } } diff --git a/precompiles/upgrade-runtime-controller/src/mock.rs b/precompiles/upgrade-runtime-controller/src/mock.rs index dc76851..7f59f51 100644 --- a/precompiles/upgrade-runtime-controller/src/mock.rs +++ b/precompiles/upgrade-runtime-controller/src/mock.rs @@ -1,16 +1,17 @@ use super::*; -use frame_support::{parameter_types, weights::Weight}; +use crate::mock::sp_api_hidden_includes_construct_runtime::hidden_include::traits::GenesisBuild; use frame_support::traits::{ConstU32, ConstU64, Contains}; +use frame_support::{parameter_types, weights::Weight}; use frame_system::EnsureRoot; +use pallet_evm::{AddressMapping, EnsureAddressNever, EnsureAddressRoot}; +use precompile_utils::precompile_set::*; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; use sp_version::RuntimeVersion; -use precompile_utils::precompile_set::*; -use pallet_evm::{EnsureAddressRoot, EnsureAddressNever, AddressMapping}; type Block = frame_system::mocking::MockBlock; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -45,6 +46,7 @@ parameter_types! { state_version: 1, }; } +pub type AccountId = stbl_core_primitives::AccountId; impl frame_system::Config for Test { type BaseCallFilter = BlockEverything; @@ -57,7 +59,7 @@ impl frame_system::Config for Test { type BlockNumber = u64; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; type RuntimeEvent = RuntimeEvent; @@ -78,7 +80,7 @@ parameter_types! { } impl pallet_upgrade_runtime_proposal::Config for Test { - type ControlOrigin = EnsureRoot; + type ControlOrigin = EnsureRoot; type MaxSizeOfCode = MaxSizeOfCode; } @@ -93,17 +95,13 @@ impl pallet_timestamp::Config for Test { type WeightInfo = (); } - -pub struct NumberAddressMapping; - -impl AddressMapping for NumberAddressMapping { - fn into_account_id(address: H160) -> u64 { - let address_bytes: [u8; 8] = (*address.as_fixed_bytes())[12..].try_into().unwrap(); - u64::from_be_bytes(address_bytes) +pub struct IdentityAddressMapping; +impl pallet_evm::AddressMapping for IdentityAddressMapping { + fn into_account_id(address: H160) -> AccountId { + address.into() } } - parameter_types! { pub BlockGasLimit: U256 = U256::max_value(); pub PrecompilesValue: Precompiles = Precompiles::new(); @@ -115,9 +113,9 @@ impl pallet_evm::Config for Test { type FeeCalculator = (); type GasWeightMapping = pallet_evm::FixedGasWeightMapping; type WeightPerGas = WeightPerGas; - type CallOrigin = EnsureAddressRoot; - type WithdrawOrigin = EnsureAddressNever; - type AddressMapping = NumberAddressMapping; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = IdentityAddressMapping; type Currency = Balances; type RuntimeEvent = RuntimeEvent; type Runner = pallet_evm::runner::stack::Runner; @@ -156,6 +154,52 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); } +type TechCommitteeInstance = pallet_collective::Instance1; + +use frame_support::weights::constants::WEIGHT_REF_TIME_PER_MILLIS; +use sp_runtime::{Perbill, Permill}; +use stbl_core_primitives::BlockNumber; + +// Block time +pub const MILLISECS_PER_BLOCK: u64 = 2000; + +pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// How much of time of block time is consumed (at most) in computing normal extrinsics +const COMPUTATION_BLOCK_TIME_RATIO: (u64, u64) = (2, 3); // 2 third parts of the block time + +const COMPUTATION_POWER_MULTIPLIER: u64 = 6; // 6 times more computation power than normal + +// how much weight for normal extrinsics could be processed in a block +pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_MILLIS + * MILLISECS_PER_BLOCK + * COMPUTATION_POWER_MULTIPLIER + * COMPUTATION_BLOCK_TIME_RATIO.0 + / COMPUTATION_BLOCK_TIME_RATIO.1, + u64::MAX, +); + +parameter_types! { + pub const CouncilMotionDuration: BlockNumber = 120; + pub const CouncilMaxProposals: u32 = 2; + pub const CouncilMaxMembers: u32 = 2; + pub const MaxProposalWeight: Weight = MAXIMUM_BLOCK_WEIGHT; +} + +impl pallet_collective::Config for Test { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = CouncilMotionDuration; + type MaxProposals = CouncilMaxProposals; + type MaxMembers = CouncilMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = pallet_collective::weights::SubstrateWeight; + type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; +} + frame_support::construct_runtime!( pub enum Test where @@ -167,12 +211,13 @@ frame_support::construct_runtime!( Timestamp: pallet_timestamp, EVM: pallet_evm, Balances: pallet_balances, + TechCommitteeCollective: pallet_collective::, } ); pub(crate) struct ExtBuilder { // endowed accounts with balances - balances: Vec<(u64, Balance)>, + balances: Vec<(AccountId, Balance)>, } impl Default for ExtBuilder { @@ -193,6 +238,13 @@ impl ExtBuilder { .assimilate_storage(&mut t) .expect("Pallet balances storage can be assimilated"); + pallet_collective::GenesisConfig:: { + members: vec![], + phantom: Default::default(), + } + .assimilate_storage(&mut t) + .expect("Pallet collective storage can be assimilated"); + let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| System::set_block_number(1)); ext diff --git a/precompiles/upgrade-runtime-controller/src/tests.rs b/precompiles/upgrade-runtime-controller/src/tests.rs index df119f2..d3c5bfe 100644 --- a/precompiles/upgrade-runtime-controller/src/tests.rs +++ b/precompiles/upgrade-runtime-controller/src/tests.rs @@ -59,6 +59,8 @@ fn owner_correctly_init() { parameter_types! { pub UnpermissionedAccount:H160 = H160::from_str("0x1000000000000000000000000000000000000000").expect("invalid address"); pub UnpermissionedAccount2:H160 = H160::from_str("0x2000000000000000000000000000000000000000").expect("invalid address"); + pub NewMember:H160 = H160::from_str("0xaafB45fB0581FC07C0F07b04b730a303469548Dc").expect("invalid address"); + pub NotOwner:H160 = H160::from_str("0x9A34381dd72d7F7ED4be83Bf31593243196f5464").expect("invalid address"); } #[test] @@ -145,7 +147,7 @@ fn claim_ownership_if_claimable() { .expect_log(log1( Precompile1, SELECTOR_LOG_NEW_OWNER, - solidity::encode_event_data(Into::::into(new_owner)) + solidity::encode_event_data(Into::::into(new_owner)), )) .execute_some(); @@ -158,15 +160,16 @@ fn claim_ownership_if_claimable() { #[test] fn test_set_block_application() { let executor = stability_test_runtime_client::new_native_or_wasm_executor(); - let mut ext = ExtBuilder::default().build(); + let mut ext = ExtBuilder::default().build(); - ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); ext.execute_with(|| { UpgradeRuntimeProposal::propose_code( - frame_system::RawOrigin::Root.into(), - stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec() - ).unwrap(); + frame_system::RawOrigin::Root.into(), + stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(), + ) + .unwrap(); let block_number = 100u32; precompiles() @@ -180,11 +183,14 @@ fn test_set_block_application() { .expect_log(log1( Precompile1, SELECTOR_SETTED__APPLICATION_BLOCK, - solidity::encode_event_data(Into::::into(block_number)) + solidity::encode_event_data(Into::::into(block_number)), )) .execute_some(); - - assert_eq!(UpgradeRuntimeProposal::get_application_block_number().unwrap(), BlockNumber::from(block_number)); + + assert_eq!( + UpgradeRuntimeProposal::get_application_block_number().unwrap(), + BlockNumber::from(block_number) + ); }); } @@ -198,8 +204,9 @@ fn test_fail_set_block_application_if_not_owner() { ext.execute_with(|| { UpgradeRuntimeProposal::propose_code( frame_system::RawOrigin::Root.into(), - stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec() - ).unwrap(); + stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(), + ) + .unwrap(); let block_number = 100u32; precompiles() @@ -224,8 +231,9 @@ fn test_reject_proposed_code() { ext.execute_with(|| { UpgradeRuntimeProposal::propose_code( frame_system::RawOrigin::Root.into(), - stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec() - ).unwrap(); + stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(), + ) + .unwrap(); precompiles() .prepare_test( @@ -233,10 +241,7 @@ fn test_reject_proposed_code() { Precompile1, PCall::reject_proposed_code {}, ) - .expect_log(log0( - Precompile1, - SELECTOR_CODE_PROPOSED_REJECTED, - )) + .expect_log(log0(Precompile1, SELECTOR_CODE_PROPOSED_REJECTED)) .execute_some(); assert!(UpgradeRuntimeProposal::get_proposed_code().is_none()); @@ -254,8 +259,9 @@ fn test_fail_reject_proposed_code_if_not_owner() { ext.execute_with(|| { UpgradeRuntimeProposal::propose_code( frame_system::RawOrigin::Root.into(), - stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec() - ).unwrap(); + stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(), + ) + .unwrap(); precompiles() .prepare_test( @@ -265,4 +271,227 @@ fn test_fail_reject_proposed_code_if_not_owner() { ) .execute_reverts(|x| x.eq_ignore_ascii_case(b"sender is not owner")); }); -} \ No newline at end of file +} + +#[test] +fn test_add_member_to_council() { + let executor = stability_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = ExtBuilder::default().build(); + + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + + ext.execute_with(|| { + precompiles() + .prepare_test( + DefaultOwner::get(), + Precompile1, + PCall::add_member_to_technical_committee { + member: NewMember::get().into(), + }, + ) + .expect_log(log1( + Precompile1, + SELECTOR_MEMBER_ADDED, + solidity::encode_event_data(Into::
::into(NewMember::get())), + )) + .execute_some(); + + precompiles() + .prepare_test( + NotOwner::get(), + Precompile1, + PCall::get_technical_committee_members {}, + ) + .execute_returns(vec![Into::
::into(NewMember::get())]); + }); +} + +#[test] +fn test_remove_member_from_council() { + let executor = stability_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = ExtBuilder::default().build(); + + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + + ext.execute_with(|| { + precompiles() + .prepare_test( + DefaultOwner::get(), + Precompile1, + PCall::add_member_to_technical_committee { + member: NewMember::get().into(), + }, + ) + .execute_some(); + + precompiles() + .prepare_test( + NotOwner::get(), + Precompile1, + PCall::get_technical_committee_members {}, + ) + .execute_returns(vec![Into::
::into(NewMember::get())]); + + precompiles() + .prepare_test( + DefaultOwner::get(), + Precompile1, + PCall::remove_member_to_technical_committee { + member: NewMember::get().into(), + }, + ) + .expect_log(log1( + Precompile1, + SELECTOR_MEMBER_REMOVED, + solidity::encode_event_data(Into::
::into(NewMember::get())), + )) + .execute_some(); + + precompiles() + .prepare_test( + NotOwner::get(), + Precompile1, + PCall::get_technical_committee_members {}, + ) + .execute_returns(Vec::
::new()); + }); +} + +#[test] +fn test_add_member_to_council_fails_if_not_owner() { + let executor = stability_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = ExtBuilder::default().build(); + + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + + ext.execute_with(|| { + precompiles() + .prepare_test( + NotOwner::get(), + Precompile1, + PCall::add_member_to_technical_committee { + member: NewMember::get().into(), + }, + ) + .execute_reverts(|x| x.eq_ignore_ascii_case(b"sender is not owner")); + }); +} + +#[test] +fn test_remove_member_from_council_fails_if_not_owner() { + let executor = stability_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = ExtBuilder::default().build(); + + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + + ext.execute_with(|| { + precompiles() + .prepare_test( + NotOwner::get(), + Precompile1, + PCall::remove_member_to_technical_committee { + member: NewMember::get().into(), + }, + ) + .execute_reverts(|x| x.eq_ignore_ascii_case(b"sender is not owner")); + }); +} + +#[test] +fn test_add_member_to_council_fails_if_already_member() { + let executor = stability_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = ExtBuilder::default().build(); + + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + + ext.execute_with(|| { + precompiles() + .prepare_test( + DefaultOwner::get(), + Precompile1, + PCall::add_member_to_technical_committee { + member: NewMember::get().into(), + }, + ) + .execute_some(); + + precompiles() + .prepare_test( + NotOwner::get(), + Precompile1, + PCall::get_technical_committee_members {}, + ) + .execute_returns(vec![Into::
::into(NewMember::get())]); + + precompiles() + .prepare_test( + DefaultOwner::get(), + Precompile1, + PCall::add_member_to_technical_committee { + member: NewMember::get().into(), + }, + ) + .execute_reverts(|x| x.eq_ignore_ascii_case(b"already a member")); + }); +} + +#[test] +fn test_remove_member_from_council_fails_if_not_member() { + let executor = stability_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = ExtBuilder::default().build(); + + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + + ext.execute_with(|| { + precompiles() + .prepare_test( + DefaultOwner::get(), + Precompile1, + PCall::add_member_to_technical_committee { + member: NewMember::get().into(), + }, + ) + .execute_some(); + + precompiles() + .prepare_test( + NotOwner::get(), + Precompile1, + PCall::get_technical_committee_members {}, + ) + .execute_returns(vec![Into::
::into(NewMember::get())]); + + precompiles() + .prepare_test( + DefaultOwner::get(), + Precompile1, + PCall::remove_member_to_technical_committee { + member: NotOwner::get().into(), + }, + ) + .execute_reverts(|x| x.eq_ignore_ascii_case(b"not a member")); + }); +} + +#[test] +fn test_get_hash_of_proposed_code() { + let executor = stability_test_runtime_client::new_native_or_wasm_executor(); + let mut ext = ExtBuilder::default().build(); + + ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new(executor)); + ext.execute_with(|| { + UpgradeRuntimeProposal::propose_code( + frame_system::RawOrigin::Root.into(), + stability_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(), + ) + .unwrap(); + + precompiles() + .prepare_test( + DefaultOwner::get(), + Precompile1, + PCall::get_hash_of_proposed_code {}, + ) + .execute_some(); + }); +}