Skip to content

Commit

Permalink
Add members to tech council from EVM precompile (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
GabrielMartinezRodriguez authored Jan 23, 2024
1 parent 83afbd5 commit e230b29
Show file tree
Hide file tree
Showing 9 changed files with 544 additions and 69 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions pallets/upgrade-runtime-proposal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -42,6 +43,10 @@ pub mod pallet {
#[pallet::getter(fn application_block_number)]
pub type ApplicationBlockNumber<T: Config> = StorageValue<_, T::BlockNumber, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn current_code_hash)]
pub type CurrentCodeHash<T: Config> = StorageValue<_, T::Hash, OptionQuery>;

#[pallet::error]
pub enum Error<T> {
/// The address received is invalid
Expand Down Expand Up @@ -154,6 +159,8 @@ pub mod pallet {
if result.is_err() {
log::error!("Failed to upgrade runtime");
} else {
let hash = Pallet::<T>::hash_of_proposed_code().unwrap();
Pallet::<T>::set_current_code_hash(hash);
log::info!("Runtime upgraded");
}

Expand All @@ -173,6 +180,18 @@ pub mod pallet {
<ApplicationBlockNumber<T>>::get()
}

pub fn get_current_code_hash() -> Option<T::Hash> {
<CurrentCodeHash<T>>::get()
}

pub fn hash_of_proposed_code() -> Option<T::Hash> {
<ProposedCode<T>>::get().map(|code| T::Hashing::hash(&code))
}

pub fn set_current_code_hash(hash: T::Hash) -> () {
<CurrentCodeHash<T>>::put(hash)
}

pub fn set_application_block_number(block_number: T::BlockNumber) {
<ApplicationBlockNumber<T>>::put(block_number);
}
Expand Down
2 changes: 1 addition & 1 deletion pallets/upgrade-runtime-proposal/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Test>;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
Expand Down
67 changes: 40 additions & 27 deletions pallets/upgrade-runtime-proposal/src/tests.rs
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -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
);
});
}

Expand Down Expand Up @@ -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());
});
}
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());
});
}
2 changes: 2 additions & 0 deletions precompiles/upgrade-runtime-controller/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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" ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
158 changes: 154 additions & 4 deletions precompiles/upgrade-runtime-controller/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand All @@ -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;
Expand Down Expand Up @@ -83,13 +87,18 @@ pub struct UpgradeRuntimeControllerPrecompile<Runtime, DefaultOwner: Get<H160> +
impl<Runtime, DefaultOwner> UpgradeRuntimeControllerPrecompile<Runtime, DefaultOwner>
where
DefaultOwner: Get<H160> + 'static,
Runtime:
pallet_upgrade_runtime_proposal::Config + pallet_timestamp::Config + pallet_evm::Config,
Runtime: pallet_upgrade_runtime_proposal::Config
+ pallet_collective::Config<pallet_collective::Instance1>
+ pallet_timestamp::Config
+ pallet_evm::Config,
Runtime::RuntimeCall: From<pallet_upgrade_runtime_proposal::Call<Runtime>>,
<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
<Runtime as pallet_timestamp::Config>::Moment: Into<U256>,
<Runtime as frame_system::Config>::BlockNumber: From<u32>,
<Runtime as frame_system::Config>::Hash: Into<H256>,
<Runtime as frame_system::Config>::AccountId: From<H160>,
<Runtime as frame_system::Config>::AccountId: Into<H160>,
{
#[precompile::public("owner()")]
#[precompile::view]
Expand Down Expand Up @@ -130,7 +139,7 @@ where
handle.context().address,
SELECTOR_LOG_TRANSFER_OWNER,
Into::<H256>::into(owner),
solidity::encode_event_data(Into::<H256>::into(target_new_owner))
solidity::encode_event_data(Into::<H256>::into(target_new_owner)),
)
.record(handle)?;

Expand Down Expand Up @@ -160,7 +169,7 @@ where
log1(
handle.context().address,
SELECTOR_LOG_NEW_OWNER,
solidity::encode_event_data(Into::<H256>::into(target_new_owner))
solidity::encode_event_data(Into::<H256>::into(target_new_owner)),
)
.record(handle)?;

Expand Down Expand Up @@ -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::<Runtime>::db_read_gas_cost())?;

let msg_sender = handle.context().caller;
let owner = OwnerStorage::<DefaultOwner>::get();

if msg_sender != owner {
return Err(revert("sender is not owner"));
}

handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let member_id: H160 = member.into();

let old_members =
pallet_collective::Pallet::<Runtime, pallet_collective::Instance1>::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::<Vec<_>>();

new_members.sort();

pallet_collective::Pallet::<Runtime, pallet_collective::Instance1>::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::<Address>::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::<Runtime>::db_read_gas_cost())?;

let msg_sender = handle.context().caller;
let owner = OwnerStorage::<DefaultOwner>::get();

if msg_sender != owner {
return Err(revert("sender is not owner"));
}

handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let member_id: H160 = member.into();
let member_account = <Runtime as frame_system::Config>::AccountId::from(member_id);

let old_members =
pallet_collective::Pallet::<Runtime, pallet_collective::Instance1>::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::<Vec<_>>();

new_members.sort();

pallet_collective::Pallet::<Runtime, pallet_collective::Instance1>::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::<Address>::into(member_id)),
)
.record(handle)?;

Ok(())
}

#[precompile::public("getTechnicalCommitteeMembers()")]
#[precompile::view]
fn get_technical_committee_members(
handle: &mut impl PrecompileHandle,
) -> EvmResult<Vec<Address>> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let members = pallet_collective::Pallet::<Runtime, pallet_collective::Instance1>::members();

Ok(members
.iter()
.map(|m| Into::<H160>::into(m.clone()))
.map(|m| Into::<Address>::into(m))
.collect())
}

#[precompile::public("getHashOfProposedCode()")]
#[precompile::view]
fn get_hash_of_proposed_code(handle: &mut impl PrecompileHandle) -> EvmResult<H256> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let hash = pallet_upgrade_runtime_proposal::Pallet::<Runtime>::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<H256> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let hash = pallet_upgrade_runtime_proposal::Pallet::<Runtime>::get_current_code_hash();

match hash {
Some(hash) => Ok(hash.into()),
None => Ok(H256::default()),
}
}
}
Loading

0 comments on commit e230b29

Please sign in to comment.