diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a73f021d29..cb4a49f926 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -791,7 +791,7 @@ jobs: - test_name: lit-di-substrate-identity-multiworker-test - test_name: lit-dr-vc-multiworker-test - test_name: lit-resume-worker - name: ${{ matrix.test_name || 'identity-multi-worker-test' }} + name: ${{ matrix.test_name }} steps: - uses: actions/checkout@v4 diff --git a/parachain/Cargo.lock b/parachain/Cargo.lock index 76618ffb6a..dfde196e33 100644 --- a/parachain/Cargo.lock +++ b/parachain/Cargo.lock @@ -11061,6 +11061,7 @@ dependencies = [ "pallet-membership", "pallet-message-queue", "pallet-multisig", + "pallet-omni-account", "pallet-teebag", "pallet-transaction-payment", "pallet-treasury", diff --git a/parachain/pallets/omni-account/src/lib.rs b/parachain/pallets/omni-account/src/lib.rs index 228a5a1d53..c7adbfbfff 100644 --- a/parachain/pallets/omni-account/src/lib.rs +++ b/parachain/pallets/omni-account/src/lib.rs @@ -1,3 +1,19 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] @@ -10,9 +26,15 @@ pub use frame_system::pallet_prelude::BlockNumberFor; pub use pallet::*; use frame_support::pallet_prelude::*; +use frame_support::{ + dispatch::{GetDispatchInfo, PostDispatchInfo}, + traits::{IsSubType, UnfilteredDispatchable}, +}; use frame_system::pallet_prelude::*; use sp_core::H256; use sp_core_hashing::blake2_256; +use sp_runtime::traits::Dispatchable; +use sp_std::boxed::Box; use sp_std::vec::Vec; #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, RuntimeDebug)] @@ -36,6 +58,21 @@ impl IDGraphHash for BoundedVec { } } +pub type MemberCount = u32; + +// Customized origin for this pallet, to: +// 1. to decouple `TEECallOrigin` and extrinsic that should be sent from `OmniAccount` origin only +// 2. allow other pallets to specify ensure_origin using this origin +// 3. leave room for more delicate control over OmniAccount in the future (e.g. multisig-like control) +#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] +#[codec(mel_bound(AccountId: MaxEncodedLen))] +pub enum RawOrigin { + // dispatched from OmniAccount T::AccountId + OmniAccount(AccountId), + // dispatched by a given number of members of the OmniAccount IDGraph from a given total + OmniAccountMembers(AccountId, MemberCount, MemberCount), +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -50,21 +87,45 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - /// The event type of this pallet. + /// The runtime origin type + type RuntimeOrigin: From> + + From>; + + /// The overarching call type + type RuntimeCall: Parameter + + Dispatchable< + RuntimeOrigin = ::RuntimeOrigin, + PostInfo = PostDispatchInfo, + > + GetDispatchInfo + + From> + + UnfilteredDispatchable::RuntimeOrigin> + + IsSubType> + + IsType<::RuntimeCall>; + + /// The event type of this pallet type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The origin which can manage the pallet. - type TEECallOrigin: EnsureOrigin; + /// The origin that represents the off-chain worker + type TEECallOrigin: EnsureOrigin<::RuntimeOrigin>; /// The maximum number of identities an id graph can have. #[pallet::constant] type MaxIDGraphLength: Get; /// AccountId converter type AccountIdConverter: AccountIdConverter; + /// The origin that represents the customised OmniAccount type + type OmniAccountOrigin: EnsureOrigin< + ::RuntimeOrigin, + Success = Self::AccountId, + >; } + pub type IDGraph = BoundedVec::MaxIDGraphLength>; + #[pallet::origin] + pub type Origin = RawOrigin<::AccountId>; + #[pallet::storage] pub type LinkedIdentityHashes = - StorageMap; + StorageMap; #[pallet::storage] #[pallet::getter(fn id_graphs)] @@ -85,6 +146,10 @@ pub mod pallet { IdentityRemoved { who: T::AccountId, identity_hashes: Vec }, /// Identity made public IdentityMadePublic { who: T::AccountId, identity_hash: H256 }, + /// Some call is dispatched as omni-account origin + DispatchedAsOmniAccount { who: T::AccountId, result: DispatchResult }, + /// Some call is dispatched as signed origin + DispatchedAsSigned { who: T::AccountId, result: DispatchResult }, } #[pallet::error] @@ -111,14 +176,56 @@ pub mod pallet { #[pallet::call] impl Pallet { + // dispatch the `call` as RawOrigin::OmniAccount #[pallet::call_index(0)] #[pallet::weight((195_000_000, DispatchClass::Normal))] + pub fn dispatch_as_omni_account( + origin: OriginFor, + account_hash: H256, + call: Box<::RuntimeCall>, + ) -> DispatchResult { + let _ = T::TEECallOrigin::ensure_origin(origin)?; + let omni_account = + LinkedIdentityHashes::::get(account_hash).ok_or(Error::::IdentityNotFound)?; + let result = call.dispatch(RawOrigin::OmniAccount(omni_account.clone()).into()); + Self::deposit_event(Event::DispatchedAsOmniAccount { + who: omni_account, + result: result.map(|_| ()).map_err(|e| e.error), + }); + Ok(()) + } + + // dispatch the `call` as the standard (frame_system) signed origin + // TODO: what about other customised origin like collective? + #[pallet::call_index(1)] + #[pallet::weight((195_000_000, DispatchClass::Normal))] + pub fn dispatch_as_signed( + origin: OriginFor, + account_hash: H256, + call: Box<::RuntimeCall>, + ) -> DispatchResult { + let _ = T::TEECallOrigin::ensure_origin(origin)?; + let omni_account = + LinkedIdentityHashes::::get(account_hash).ok_or(Error::::IdentityNotFound)?; + let result = + call.dispatch(frame_system::RawOrigin::Signed(omni_account.clone()).into()); + Self::deposit_event(Event::DispatchedAsSigned { + who: omni_account, + result: result.map(|_| ()).map_err(|e| e.error), + }); + Ok(()) + } + + #[pallet::call_index(2)] + #[pallet::weight((195_000_000, DispatchClass::Normal))] pub fn link_identity( origin: OriginFor, who: Identity, member_account: IDGraphMember, maybe_id_graph_hash: Option, ) -> DispatchResult { + // We can't use `T::OmniAccountOrigin` here as the ownership of member account needs to + // be firstly validated by the TEE-worker before dispatching the extrinsic let _ = T::TEECallOrigin::ensure_origin(origin)?; ensure!( !LinkedIdentityHashes::::contains_key(member_account.hash), @@ -138,7 +245,7 @@ pub mod pallet { .try_push(member_account) .map_err(|_| Error::::IDGraphLenLimitReached)?; - LinkedIdentityHashes::::insert(identity_hash, ()); + LinkedIdentityHashes::::insert(identity_hash, who_account_id.clone()); IDGraphHashes::::insert(who_account_id.clone(), id_graph.graph_hash()); IDGraphs::::insert(who_account_id.clone(), id_graph); @@ -150,23 +257,17 @@ pub mod pallet { Ok(()) } - #[pallet::call_index(4)] + #[pallet::call_index(3)] #[pallet::weight((195_000_000, DispatchClass::Normal))] pub fn remove_identities( origin: OriginFor, - who: Identity, identity_hashes: Vec, ) -> DispatchResult { - let _ = T::TEECallOrigin::ensure_origin(origin)?; + let who = T::OmniAccountOrigin::ensure_origin(origin)?; ensure!(!identity_hashes.is_empty(), Error::::IdentitiesEmpty); - let who_account_id = match T::AccountIdConverter::convert(&who) { - Some(account_id) => account_id, - None => return Err(Error::::InvalidIdentity.into()), - }; - let mut id_graph_members = - IDGraphs::::get(&who_account_id).ok_or(Error::::UnknownIDGraph)?; + IDGraphs::::get(&who).ok_or(Error::::UnknownIDGraph)?; id_graph_members.retain(|member| { if identity_hashes.contains(&member.hash) { @@ -178,43 +279,37 @@ pub mod pallet { }); if id_graph_members.is_empty() { - IDGraphs::::remove(&who_account_id); + IDGraphs::::remove(&who); } else { - IDGraphs::::insert(who_account_id.clone(), id_graph_members); + IDGraphs::::insert(who.clone(), id_graph_members); } - Self::deposit_event(Event::IdentityRemoved { who: who_account_id, identity_hashes }); + Self::deposit_event(Event::IdentityRemoved { who, identity_hashes }); Ok(()) } - #[pallet::call_index(5)] + #[pallet::call_index(4)] #[pallet::weight((195_000_000, DispatchClass::Normal))] pub fn make_identity_public( origin: OriginFor, - who: Identity, identity_hash: H256, public_identity: MemberIdentity, ) -> DispatchResult { - let _ = T::TEECallOrigin::ensure_origin(origin)?; + let who = T::OmniAccountOrigin::ensure_origin(origin)?; ensure!(public_identity.is_public(), Error::::IdentityIsPrivate); - let who_account_id = match T::AccountIdConverter::convert(&who) { - Some(account_id) => account_id, - None => return Err(Error::::InvalidIdentity.into()), - }; - let mut id_graph_members = - IDGraphs::::get(&who_account_id).ok_or(Error::::UnknownIDGraph)?; + IDGraphs::::get(&who).ok_or(Error::::UnknownIDGraph)?; let id_graph_link = id_graph_members .iter_mut() .find(|member| member.hash == identity_hash) .ok_or(Error::::IdentityNotFound)?; id_graph_link.id = public_identity; - IDGraphs::::insert(who_account_id.clone(), id_graph_members); + IDGraphs::::insert(who.clone(), id_graph_members); - Self::deposit_event(Event::IdentityMadePublic { who: who_account_id, identity_hash }); + Self::deposit_event(Event::IdentityMadePublic { who, identity_hash }); Ok(()) } @@ -270,10 +365,31 @@ pub mod pallet { hash: owner_identity_hash, }) .map_err(|_| Error::::IDGraphLenLimitReached)?; - LinkedIdentityHashes::::insert(owner_identity_hash, ()); + LinkedIdentityHashes::::insert(owner_identity_hash, owner_account_id.clone()); IDGraphs::::insert(owner_account_id.clone(), id_graph_members.clone()); Ok(id_graph_members) } } } + +pub struct EnsureOmniAccount(PhantomData); +impl, O>> + From>, AccountId: Decode> + EnsureOrigin for EnsureOmniAccount +{ + type Success = AccountId; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::OmniAccount(id) => Ok(id), + r => Err(O::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + let zero_account_id = + AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()) + .expect("infinite length input; no invalid inputs for type; qed"); + Ok(O::from(RawOrigin::OmniAccount(zero_account_id))) + } +} diff --git a/parachain/pallets/omni-account/src/mock.rs b/parachain/pallets/omni-account/src/mock.rs index fd2cdabf32..8a0178af75 100644 --- a/parachain/pallets/omni-account/src/mock.rs +++ b/parachain/pallets/omni-account/src/mock.rs @@ -1,4 +1,20 @@ -use crate::{self as pallet_omni_account}; +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{self as pallet_omni_account, EnsureOmniAccount}; use core_primitives::Identity; use frame_support::{ assert_ok, @@ -152,10 +168,13 @@ impl pallet_omni_account::AccountIdConverter for IdentityToAccountI } impl pallet_omni_account::Config for TestRuntime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type TEECallOrigin = EnsureEnclaveSigner; type MaxIDGraphLength = ConstU32<3>; type AccountIdConverter = IdentityToAccountIdConverter; + type OmniAccountOrigin = EnsureOmniAccount; } pub fn get_tee_signer() -> SystemAccountId { @@ -163,11 +182,12 @@ pub fn get_tee_signer() -> SystemAccountId { } pub fn new_test_ext() -> sp_io::TestExternalities { - let system = frame_system::GenesisConfig::::default(); - let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig { system, ..Default::default() } - .build_storage() - .unwrap() - .into(); + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { balances: vec![(alice(), 10)] } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext: sp_io::TestExternalities = t.into(); ext.execute_with(|| { System::set_block_number(1); let signer = get_tee_signer(); diff --git a/parachain/pallets/omni-account/src/tests.rs b/parachain/pallets/omni-account/src/tests.rs index 4b8b0c795e..effa5df4f6 100644 --- a/parachain/pallets/omni-account/src/tests.rs +++ b/parachain/pallets/omni-account/src/tests.rs @@ -1,9 +1,43 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + use crate::{mock::*, IDGraphs, LinkedIdentityHashes, *}; use core_primitives::Identity; use frame_support::{assert_noop, assert_ok}; -use sp_runtime::traits::BadOrigin; +use sp_runtime::{traits::BadOrigin, ModuleError}; use sp_std::vec; +fn remove_identity_call(hashes: Vec) -> Box { + let call = RuntimeCall::OmniAccount(crate::Call::remove_identities { identity_hashes: hashes }); + Box::new(call) +} + +fn make_identity_public_call(hash: H256, id: MemberIdentity) -> Box { + let call = RuntimeCall::OmniAccount(crate::Call::make_identity_public { + identity_hash: hash, + public_identity: id, + }); + Box::new(call) +} + +fn make_balance_transfer_call(dest: AccountId, value: Balance) -> Box { + let call = RuntimeCall::Balances(pallet_balances::Call::transfer { dest, value }); + Box::new(call) +} + #[test] fn link_identity_works() { new_test_ext().execute_with(|| { @@ -326,12 +360,38 @@ fn remove_identity_works() { member_account.clone(), None )); - assert_ok!(OmniAccount::remove_identities( + + // normal signed origin should give `BadOrigin`, no matter + // it's from TEE-worker, or omni-account itself + assert_noop!( + OmniAccount::remove_identities( + RuntimeOrigin::signed(tee_signer.clone()), + identities_to_remove.clone() + ), + sp_runtime::DispatchError::BadOrigin + ); + + assert_noop!( + OmniAccount::remove_identities( + RuntimeOrigin::signed(who.clone()), + identities_to_remove.clone() + ), + sp_runtime::DispatchError::BadOrigin + ); + + let call = remove_identity_call(identities_to_remove.clone()); + assert_ok!(OmniAccount::dispatch_as_omni_account( RuntimeOrigin::signed(tee_signer.clone()), - who_identity.clone(), - identities_to_remove.clone() + who_identity_hash, + call )); - System::assert_last_event( + + System::assert_has_event( + Event::DispatchedAsOmniAccount { who: who.clone(), result: DispatchResult::Ok(()) } + .into(), + ); + + System::assert_has_event( Event::IdentityRemoved { who: who.clone(), identity_hashes: identities_to_remove } .into(), ); @@ -345,15 +405,12 @@ fn remove_identity_works() { assert_eq!(IDGraphs::::get(&who).unwrap(), expected_id_graph); assert!(!LinkedIdentityHashes::::contains_key(identity_hash)); - assert_ok!(OmniAccount::remove_identities( + let call = remove_identity_call(vec![who_identity_hash]); + assert_ok!(OmniAccount::dispatch_as_omni_account( RuntimeOrigin::signed(tee_signer.clone()), - who_identity.clone(), - vec![who_identity_hash], + who_identity_hash, + call )); - System::assert_last_event( - Event::IdentityRemoved { who: who.clone(), identity_hashes: vec![who_identity_hash] } - .into(), - ); assert!(!IDGraphs::::contains_key(&who)); }); @@ -363,33 +420,37 @@ fn remove_identity_works() { fn remove_identity_empty_identity_check_works() { new_test_ext().execute_with(|| { let tee_signer = get_tee_signer(); - let who = Identity::from(alice()); + let who = alice(); + let who_identity = Identity::from(who.clone()); + let who_identity_hash = who_identity.hash().unwrap(); assert_ok!(OmniAccount::link_identity( RuntimeOrigin::signed(tee_signer.clone()), - who.clone(), + who_identity, IDGraphMember { id: MemberIdentity::Private(vec![1, 2, 3]), hash: H256::from(blake2_256(&[1, 2, 3])), }, None )); - assert_noop!( - OmniAccount::remove_identities(RuntimeOrigin::signed(tee_signer.clone()), who, vec![],), - Error::::IdentitiesEmpty - ); - }); -} -#[test] -fn remove_identity_origin_check_works() { - new_test_ext().execute_with(|| { - let who = Identity::from(alice()); - let identities_to_remove = vec![H256::from(blake2_256(&[1, 2, 3]))]; - - assert_noop!( - OmniAccount::remove_identities(RuntimeOrigin::signed(bob()), who, identities_to_remove), - BadOrigin + let call = remove_identity_call(vec![]); + // execution itself is ok, but error is shown in the dispatch result + assert_ok!(OmniAccount::dispatch_as_omni_account( + RuntimeOrigin::signed(tee_signer.clone()), + who_identity_hash, + call + )); + System::assert_has_event( + Event::DispatchedAsOmniAccount { + who, + result: Err(DispatchError::Module(ModuleError { + index: 5, + error: [6, 0, 0, 0], + message: Some("IdentitiesEmpty"), + })), + } + .into(), ); }); } @@ -400,6 +461,7 @@ fn make_identity_public_works() { let tee_signer = get_tee_signer(); let who = alice(); let who_identity = Identity::from(who.clone()); + let who_identity_hash = who_identity.hash().unwrap(); let private_identity = MemberIdentity::Private(vec![1, 2, 3]); let public_identity = MemberIdentity::Public(Identity::from(bob())); @@ -422,13 +484,19 @@ fn make_identity_public_works() { ]); assert_eq!(IDGraphs::::get(&who).unwrap(), expected_id_graph); - assert_ok!(OmniAccount::make_identity_public( + let call = make_identity_public_call(identity_hash, public_identity.clone()); + assert_ok!(OmniAccount::dispatch_as_omni_account( RuntimeOrigin::signed(tee_signer.clone()), - who_identity.clone(), - identity_hash, - public_identity.clone() + who_identity_hash, + call )); - System::assert_last_event( + + System::assert_has_event( + Event::DispatchedAsOmniAccount { who: who.clone(), result: DispatchResult::Ok(()) } + .into(), + ); + + System::assert_has_event( Event::IdentityMadePublic { who: who.clone(), identity_hash }.into(), ); @@ -443,31 +511,13 @@ fn make_identity_public_works() { }); } -#[test] -fn make_identity_public_origin_check_works() { - new_test_ext().execute_with(|| { - let who = Identity::from(alice()); - let identity = Identity::from(bob()); - let identity_hash = identity.hash().unwrap(); - let public_identity = MemberIdentity::Public(identity.clone()); - - assert_noop!( - OmniAccount::make_identity_public( - RuntimeOrigin::signed(bob()), - who, - identity_hash, - public_identity - ), - BadOrigin - ); - }); -} - #[test] fn make_identity_public_identity_not_found_works() { new_test_ext().execute_with(|| { let tee_signer = get_tee_signer(); - let who = Identity::from(alice()); + let who = alice(); + let who_identity = Identity::from(who.clone()); + let who_identity_hash = who_identity.hash().unwrap(); let private_identity = MemberIdentity::Private(vec![1, 2, 3]); let identity = Identity::from(bob()); @@ -475,19 +525,19 @@ fn make_identity_public_identity_not_found_works() { let identity_hash = H256::from(blake2_256(&Identity::from(bob()).to_did().unwrap().encode())); + let call = make_identity_public_call(identity_hash, public_identity.clone()); assert_noop!( - OmniAccount::make_identity_public( + OmniAccount::dispatch_as_omni_account( RuntimeOrigin::signed(tee_signer.clone()), - who.clone(), - identity_hash, - public_identity.clone() + who_identity_hash, + call ), - Error::::UnknownIDGraph + Error::::IdentityNotFound ); assert_ok!(OmniAccount::link_identity( RuntimeOrigin::signed(tee_signer.clone()), - who.clone(), + who_identity, IDGraphMember { id: private_identity.clone(), hash: identity_hash }, None )); @@ -497,14 +547,22 @@ fn make_identity_public_identity_not_found_works() { let other_identity_hash = H256::from(blake2_256(&charlie_identity.to_did().unwrap().encode())); - assert_noop!( - OmniAccount::make_identity_public( - RuntimeOrigin::signed(tee_signer), + let call = make_identity_public_call(other_identity_hash, other_identity); + assert_ok!(OmniAccount::dispatch_as_omni_account( + RuntimeOrigin::signed(tee_signer.clone()), + who_identity_hash, + call + )); + System::assert_has_event( + Event::DispatchedAsOmniAccount { who, - other_identity_hash, - other_identity, - ), - Error::::IdentityNotFound + result: Err(DispatchError::Module(ModuleError { + index: 5, + error: [2, 0, 0, 0], + message: Some("IdentityNotFound"), + })), + } + .into(), ); }); } @@ -513,26 +571,68 @@ fn make_identity_public_identity_not_found_works() { fn make_identity_public_identity_is_private_check_works() { new_test_ext().execute_with(|| { let tee_signer = get_tee_signer(); - let who = Identity::from(alice()); + let who = alice(); + let who_identity = Identity::from(who.clone()); + let who_identity_hash = who_identity.hash().unwrap(); let private_identity = MemberIdentity::Private(vec![1, 2, 3]); let identity_hash = Identity::from(bob()).hash().unwrap(); assert_ok!(OmniAccount::link_identity( RuntimeOrigin::signed(tee_signer.clone()), - who.clone(), + who_identity, IDGraphMember { id: private_identity.clone(), hash: identity_hash }, None )); - assert_noop!( - OmniAccount::make_identity_public( - RuntimeOrigin::signed(tee_signer), + let call = make_identity_public_call(identity_hash, private_identity); + assert_ok!(OmniAccount::dispatch_as_omni_account( + RuntimeOrigin::signed(tee_signer.clone()), + who_identity_hash, + call + )); + System::assert_has_event( + Event::DispatchedAsOmniAccount { who, - identity_hash, - private_identity, - ), - Error::::IdentityIsPrivate + result: Err(DispatchError::Module(ModuleError { + index: 5, + error: [5, 0, 0, 0], + message: Some("IdentityIsPrivate"), + })), + } + .into(), ); }); } + +#[test] +fn dispatch_as_signed_works() { + new_test_ext().execute_with(|| { + let tee_signer = get_tee_signer(); + let who = alice(); + let who_identity = Identity::from(who.clone()); + let who_identity_hash = who_identity.hash().unwrap(); + + let private_identity = MemberIdentity::Private(vec![1, 2, 3]); + let identity_hash = Identity::from(bob()).hash().unwrap(); + + assert_ok!(OmniAccount::link_identity( + RuntimeOrigin::signed(tee_signer.clone()), + who_identity, + IDGraphMember { id: private_identity.clone(), hash: identity_hash }, + None + )); + + let call = make_balance_transfer_call(bob(), 5); + assert_ok!(OmniAccount::dispatch_as_signed( + RuntimeOrigin::signed(tee_signer), + who_identity_hash, + call + )); + System::assert_has_event( + Event::DispatchedAsSigned { who, result: DispatchResult::Ok(()) }.into(), + ); + + assert_eq!(Balances::free_balance(bob()), 5); + }); +} diff --git a/parachain/runtime/common/Cargo.toml b/parachain/runtime/common/Cargo.toml index f93b7750a2..ffd75db923 100644 --- a/parachain/runtime/common/Cargo.toml +++ b/parachain/runtime/common/Cargo.toml @@ -50,6 +50,7 @@ cumulus-test-relay-sproof-builder = { workspace = true, optional = true } pallet-asset-manager = { workspace = true } pallet-extrinsic-filter = { workspace = true } pallet-group = { workspace = true } +pallet-omni-account = { workspace = true } pallet-teebag = { workspace = true } [features] @@ -89,6 +90,7 @@ std = [ "core-primitives/std", "pallet-asset-manager/std", "pallet-extrinsic-filter/std", + "pallet-omni-account/std", "pallet-teebag/std", "orml-xtokens/std", ] diff --git a/parachain/runtime/common/src/lib.rs b/parachain/runtime/common/src/lib.rs index d04b9f7491..316fcc1ca1 100644 --- a/parachain/runtime/common/src/lib.rs +++ b/parachain/runtime/common/src/lib.rs @@ -327,3 +327,5 @@ where Ok(frame_system::RawOrigin::Signed(signer).into()) } } + +pub type EnsureOmniAccount = pallet_omni_account::EnsureOmniAccount; diff --git a/parachain/runtime/litentry/Cargo.toml b/parachain/runtime/litentry/Cargo.toml index 3b3a3bc1ba..86aab76cea 100644 --- a/parachain/runtime/litentry/Cargo.toml +++ b/parachain/runtime/litentry/Cargo.toml @@ -170,6 +170,7 @@ runtime-benchmarks = [ "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", "pallet-score-staking/runtime-benchmarks", + "pallet-omni-account/runtime-benchmarks", ] std = [ "parity-scale-codec/std", @@ -255,6 +256,7 @@ std = [ "pallet-bridge-transfer/std", "pallet-extrinsic-filter/std", "pallet-bitacross/std", + "pallet-omni-account/std", "pallet-identity-management/std", "pallet-score-staking/std", "pallet-teebag/std", @@ -315,4 +317,5 @@ try-runtime = [ "pallet-xcm/try-runtime", "parachain-info/try-runtime", "pallet-score-staking/try-runtime", + "pallet-omni-account/try-runtime", ] diff --git a/parachain/runtime/litentry/src/lib.rs b/parachain/runtime/litentry/src/lib.rs index 1d81366913..2f42a4433a 100644 --- a/parachain/runtime/litentry/src/lib.rs +++ b/parachain/runtime/litentry/src/lib.rs @@ -66,13 +66,13 @@ pub use runtime_common::currency::*; use runtime_common::{ impl_runtime_transaction_payment_fees, prod_or_fast, BlockHashCount, BlockLength, CouncilInstance, CouncilMembershipInstance, DeveloperCommitteeInstance, - DeveloperCommitteeMembershipInstance, EnsureEnclaveSigner, EnsureRootOrAllCouncil, - EnsureRootOrAllTechnicalCommittee, EnsureRootOrHalfCouncil, EnsureRootOrHalfTechnicalCommittee, - EnsureRootOrTwoThirdsCouncil, EnsureRootOrTwoThirdsTechnicalCommittee, - IMPExtrinsicWhitelistInstance, NegativeImbalance, RuntimeBlockWeights, SlowAdjustingFeeUpdate, - TechnicalCommitteeInstance, TechnicalCommitteeMembershipInstance, - VCMPExtrinsicWhitelistInstance, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO, WEIGHT_PER_GAS, - WEIGHT_TO_FEE_FACTOR, + DeveloperCommitteeMembershipInstance, EnsureEnclaveSigner, EnsureOmniAccount, + EnsureRootOrAllCouncil, EnsureRootOrAllTechnicalCommittee, EnsureRootOrHalfCouncil, + EnsureRootOrHalfTechnicalCommittee, EnsureRootOrTwoThirdsCouncil, + EnsureRootOrTwoThirdsTechnicalCommittee, IMPExtrinsicWhitelistInstance, NegativeImbalance, + RuntimeBlockWeights, SlowAdjustingFeeUpdate, TechnicalCommitteeInstance, + TechnicalCommitteeMembershipInstance, VCMPExtrinsicWhitelistInstance, MAXIMUM_BLOCK_WEIGHT, + NORMAL_DISPATCH_RATIO, WEIGHT_PER_GAS, WEIGHT_TO_FEE_FACTOR, }; use xcm_config::{XcmConfig, XcmOriginToTransactDispatchOrigin}; @@ -960,10 +960,13 @@ impl pallet_omni_account::AccountIdConverter for IdentityToAccountIdCon } impl pallet_omni_account::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type TEECallOrigin = EnsureEnclaveSigner; type MaxIDGraphLength = ConstU32<64>; type AccountIdConverter = IdentityToAccountIdConverter; + type OmniAccountOrigin = EnsureOmniAccount; } impl pallet_bitacross::Config for Runtime { @@ -1316,7 +1319,8 @@ impl Contains for NormalModeFilter { RuntimeCall::AssetsHandler(_) | RuntimeCall::Bitacross(_) | RuntimeCall::EvmAssertions(_) | - RuntimeCall::ScoreStaking(_) + RuntimeCall::ScoreStaking(_) | + RuntimeCall::OmniAccount(_) ) } } diff --git a/parachain/runtime/paseo/Cargo.toml b/parachain/runtime/paseo/Cargo.toml index eb194daa05..73f1ac8494 100644 --- a/parachain/runtime/paseo/Cargo.toml +++ b/parachain/runtime/paseo/Cargo.toml @@ -173,6 +173,7 @@ runtime-benchmarks = [ "pallet-vc-management/runtime-benchmarks", "pallet-account-fix/runtime-benchmarks", "pallet-score-staking/runtime-benchmarks", + "pallet-omni-account/runtime-benchmarks", ] std = [ "parity-scale-codec/std", @@ -266,6 +267,7 @@ std = [ "pallet-extrinsic-filter/std", "pallet-group/std", "pallet-identity-management/std", + "pallet-omni-account/std", "pallet-score-staking/std", "pallet-teebag/std", "pallet-vc-management/std", @@ -327,4 +329,5 @@ try-runtime = [ "pallet-vesting/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", + "pallet-omni-account/try-runtime", ] diff --git a/parachain/runtime/paseo/src/lib.rs b/parachain/runtime/paseo/src/lib.rs index 1b77269ae2..98a157165b 100644 --- a/parachain/runtime/paseo/src/lib.rs +++ b/parachain/runtime/paseo/src/lib.rs @@ -77,7 +77,7 @@ pub use runtime_common::currency::*; use runtime_common::{ impl_runtime_transaction_payment_fees, prod_or_fast, BlockHashCount, BlockLength, CouncilInstance, CouncilMembershipInstance, DeveloperCommitteeInstance, - DeveloperCommitteeMembershipInstance, EnsureRootOrAllCouncil, + DeveloperCommitteeMembershipInstance, EnsureOmniAccount, EnsureRootOrAllCouncil, EnsureRootOrAllTechnicalCommittee, EnsureRootOrHalfCouncil, EnsureRootOrHalfTechnicalCommittee, EnsureRootOrTwoThirdsCouncil, EnsureRootOrTwoThirdsTechnicalCommittee, IMPExtrinsicWhitelistInstance, NegativeImbalance, RuntimeBlockWeights, SlowAdjustingFeeUpdate, @@ -1003,10 +1003,13 @@ impl pallet_omni_account::AccountIdConverter for IdentityToAccountIdCon } impl pallet_omni_account::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type TEECallOrigin = EnsureEnclaveSigner; type MaxIDGraphLength = ConstU32<64>; type AccountIdConverter = IdentityToAccountIdConverter; + type OmniAccountOrigin = EnsureOmniAccount; } impl pallet_bitacross::Config for Runtime { @@ -1377,7 +1380,8 @@ impl Contains for NormalModeFilter { RuntimeCall::AssetsHandler(_) | RuntimeCall::Bitacross(_) | RuntimeCall::EvmAssertions(_) | - RuntimeCall::ScoreStaking(_) + RuntimeCall::ScoreStaking(_) | + RuntimeCall::OmniAccount(_) ) } } diff --git a/parachain/runtime/rococo/Cargo.toml b/parachain/runtime/rococo/Cargo.toml index 231467f5bf..fdaae91906 100644 --- a/parachain/runtime/rococo/Cargo.toml +++ b/parachain/runtime/rococo/Cargo.toml @@ -173,6 +173,7 @@ runtime-benchmarks = [ "pallet-vc-management/runtime-benchmarks", "pallet-account-fix/runtime-benchmarks", "pallet-score-staking/runtime-benchmarks", + "pallet-omni-account/runtime-benchmarks", ] std = [ "parity-scale-codec/std", @@ -266,6 +267,7 @@ std = [ "pallet-extrinsic-filter/std", "pallet-group/std", "pallet-identity-management/std", + "pallet-omni-account/std", "pallet-score-staking/std", "pallet-teebag/std", "pallet-vc-management/std", @@ -327,4 +329,5 @@ try-runtime = [ "pallet-vesting/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", + "pallet-omni-account/try-runtime", ] diff --git a/parachain/runtime/rococo/src/lib.rs b/parachain/runtime/rococo/src/lib.rs index faf7106e51..5d2192564a 100644 --- a/parachain/runtime/rococo/src/lib.rs +++ b/parachain/runtime/rococo/src/lib.rs @@ -76,7 +76,7 @@ pub use runtime_common::currency::*; use runtime_common::{ impl_runtime_transaction_payment_fees, prod_or_fast, BlockHashCount, BlockLength, CouncilInstance, CouncilMembershipInstance, DeveloperCommitteeInstance, - DeveloperCommitteeMembershipInstance, EnsureRootOrAllCouncil, + DeveloperCommitteeMembershipInstance, EnsureOmniAccount, EnsureRootOrAllCouncil, EnsureRootOrAllTechnicalCommittee, EnsureRootOrHalfCouncil, EnsureRootOrHalfTechnicalCommittee, EnsureRootOrTwoThirdsCouncil, EnsureRootOrTwoThirdsTechnicalCommittee, IMPExtrinsicWhitelistInstance, NegativeImbalance, RuntimeBlockWeights, SlowAdjustingFeeUpdate, @@ -1002,10 +1002,13 @@ impl pallet_omni_account::AccountIdConverter for IdentityToAccountIdCon } impl pallet_omni_account::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type TEECallOrigin = EnsureEnclaveSigner; type MaxIDGraphLength = ConstU32<64>; type AccountIdConverter = IdentityToAccountIdConverter; + type OmniAccountOrigin = EnsureOmniAccount; } impl pallet_bitacross::Config for Runtime { @@ -1376,7 +1379,8 @@ impl Contains for NormalModeFilter { RuntimeCall::AssetsHandler(_) | RuntimeCall::Bitacross(_) | RuntimeCall::EvmAssertions(_) | - RuntimeCall::ScoreStaking(_) + RuntimeCall::ScoreStaking(_) | + RuntimeCall::OmniAccount(_) ) } }