Skip to content

Commit f6a0908

Browse files
authored
feat: support gatekeeping web3names and account linkings (#813)
Fixes https://github.com/KILTprotocol/ticket/issues/3691. This PR allows to dynamically (i.e., via the sudo origin) elect an account as the only authorised to submit `claim` extrinsic for any web3name pallet deployment, and `associate_account` and `associate_sender` extrinsics for any did-lookup deployment. I also updated our dotnames and unique linking deployments for both our runtimes to use this new feature, althought I had to rely on a trick with `storage_alias`es to avoid introducing a new pallet just for this. Unfortunately, the [pallet-parameters](https://github.com/paritytech/polkadot-sdk/tree/master/substrate/frame/parameters) was only introduced in 1.8.0, so we might want to migrate to using that once we update our codebase to 1.8.0. For now, I could not think of any other way to implement this feature without touching the pallets storage entries, which I did not want to. For both our runtimes, these are the two new storage keys introduced: * `0x8ea135058ec16554c8e3d230d658fbffd30ff375811804de60521a1654f58ebb` for the dotnames deployment authorization * `0x41a63f711fa40ef5e1dc8f0ac115a906d4378bcb7f1d95ba1124c2140bfccdba` for the unique linking deployment authorization These values can be updated with a `system.setStorage(key, value)` call, which must specify an account ID as the sole submitter of the extrinsics specified above. A `system.killStorage(keys)` call will set the relative entry to `None`, which means no gating is enforced and anyone can create. This is the default for our web3name and did linking deployments. **Important: when deploying the new runtime, we will also need to set the storage value for these entries, and we don't need to wait for the new runtime to be live as writing it earlier has no effect on the rest of the runtime**. ## How to test 1. Spin up a chopsticks Peregrine setup 2. Set a desired account as the sole allowed submitter for dotnames * E.g., This call sets it to the sudo `0x000404808ea135058ec16554c8e3d230d658fbffd30ff375811804de60521a1654f58ebb80921cbc0ffe09a865dbf4ae1d0410aa17c656881fe86666da0f97939e3701b674` 4. Try to claim a dotname with any DID while submitting the tx with an account different than the sudo account: this will not work 5. Try again with the sudo account and it will work. 6. Remove the authorised account with `killStorage` * This call removes it: `0x000504808ea135058ec16554c8e3d230d658fbffd30ff375811804de60521a1654f58ebb` 8. Try again with the first account: it will now work.
1 parent fe579d6 commit f6a0908

File tree

16 files changed

+194
-50
lines changed

16 files changed

+194
-50
lines changed

dip-template/runtimes/dip-provider/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ impl did::Config for Runtime {
425425
}
426426

427427
impl pallet_did_lookup::Config for Runtime {
428+
type AssociateOrigin = Self::EnsureOrigin;
428429
type BalanceMigrationManager = ();
429430
type Currency = Balances;
430431
type Deposit = ConstU128<UNIT>;
@@ -441,6 +442,7 @@ pub type Web3Name = runtime_common::Web3Name<3, 32>;
441442
impl pallet_web3_names::Config for Runtime {
442443
type BalanceMigrationManager = ();
443444
type BanOrigin = EnsureRoot<AccountId>;
445+
type ClaimOrigin = Self::OwnerOrigin;
444446
type Currency = Balances;
445447
type Deposit = ConstU128<UNIT>;
446448
type MaxNameLength = ConstU32<32>;

pallets/did/src/origin.rs

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use frame_support::traits::{EnsureOrigin, EnsureOriginWithArg};
2020
use kilt_support::traits::CallSources;
2121
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
2222
use scale_info::TypeInfo;
23+
use sp_core::Get;
2324
use sp_runtime::RuntimeDebug;
2425
use sp_std::marker::PhantomData;
2526

@@ -36,19 +37,43 @@ impl<DidIdentifier, AccountId> DidRawOrigin<DidIdentifier, AccountId> {
3637
}
3738
}
3839

39-
pub struct EnsureDidOrigin<DidIdentifier, AccountId>(PhantomData<(DidIdentifier, AccountId)>);
40+
impl<DidIdentifier: Clone, AccountId: Clone> CallSources<AccountId, DidIdentifier>
41+
for DidRawOrigin<DidIdentifier, AccountId>
42+
{
43+
fn sender(&self) -> AccountId {
44+
self.submitter.clone()
45+
}
4046

41-
impl<OuterOrigin, DidIdentifier, AccountId> EnsureOrigin<OuterOrigin> for EnsureDidOrigin<DidIdentifier, AccountId>
47+
fn subject(&self) -> DidIdentifier {
48+
self.id.clone()
49+
}
50+
}
51+
52+
pub struct EnsureDidOrigin<DidIdentifier, AccountId, ExpectedSubmitter = ()>(
53+
PhantomData<(DidIdentifier, AccountId, ExpectedSubmitter)>,
54+
);
55+
56+
impl<OuterOrigin, DidIdentifier, AccountId, ExpectedSubmitter> EnsureOrigin<OuterOrigin>
57+
for EnsureDidOrigin<DidIdentifier, AccountId, ExpectedSubmitter>
4258
where
4359
OuterOrigin: Into<Result<DidRawOrigin<DidIdentifier, AccountId>, OuterOrigin>>
44-
+ From<DidRawOrigin<DidIdentifier, AccountId>>,
60+
+ From<DidRawOrigin<DidIdentifier, AccountId>>
61+
+ Clone,
4562
DidIdentifier: From<AccountId>,
46-
AccountId: Clone + Decode,
63+
AccountId: Clone + Decode + PartialEq,
64+
ExpectedSubmitter: Get<Option<AccountId>>,
4765
{
4866
type Success = DidRawOrigin<DidIdentifier, AccountId>;
4967

5068
fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin> {
51-
o.into()
69+
let did_raw_origin = o.clone().into()?;
70+
// Origin check succeeds if no authorized account is configured, or if the one
71+
// configured matches the tx submitter. Fails otherwise.
72+
match ExpectedSubmitter::get() {
73+
None => Ok(did_raw_origin),
74+
Some(authorised_submitter) if authorised_submitter == did_raw_origin.submitter => Ok(did_raw_origin),
75+
_ => Err(o),
76+
}
5277
}
5378

5479
#[cfg(feature = "runtime-benchmarks")]
@@ -63,8 +88,8 @@ where
6388
}
6489
}
6590

66-
impl<OuterOrigin, DidIdentifier, AccountId> EnsureOriginWithArg<OuterOrigin, DidIdentifier>
67-
for EnsureDidOrigin<DidIdentifier, AccountId>
91+
impl<OuterOrigin, DidIdentifier, AccountId, ExpectedSubmitter> EnsureOriginWithArg<OuterOrigin, DidIdentifier>
92+
for EnsureDidOrigin<DidIdentifier, AccountId, ExpectedSubmitter>
6893
where
6994
OuterOrigin: Into<Result<DidRawOrigin<DidIdentifier, AccountId>, OuterOrigin>>
7095
+ From<DidRawOrigin<DidIdentifier, AccountId>>
@@ -95,22 +120,10 @@ where
95120
}
96121
}
97122

98-
impl<DidIdentifier: Clone, AccountId: Clone> CallSources<AccountId, DidIdentifier>
99-
for DidRawOrigin<DidIdentifier, AccountId>
100-
{
101-
fn sender(&self) -> AccountId {
102-
self.submitter.clone()
103-
}
104-
105-
fn subject(&self) -> DidIdentifier {
106-
self.id.clone()
107-
}
108-
}
109-
110123
#[cfg(feature = "runtime-benchmarks")]
111-
impl<OuterOrigin, AccountId, DidIdentifier>
124+
impl<OuterOrigin, AccountId, DidIdentifier, ExpectedSubmitter>
112125
kilt_support::traits::GenerateBenchmarkOrigin<OuterOrigin, AccountId, DidIdentifier>
113-
for EnsureDidOrigin<DidIdentifier, AccountId>
126+
for EnsureDidOrigin<DidIdentifier, AccountId, ExpectedSubmitter>
114127
where
115128
OuterOrigin: Into<Result<DidRawOrigin<DidIdentifier, AccountId>, OuterOrigin>>
116129
+ From<DidRawOrigin<DidIdentifier, AccountId>>,
@@ -128,11 +141,15 @@ mod tests {
128141
#[cfg(feature = "runtime-benchmarks")]
129142
#[test]
130143
pub fn successful_origin() {
131-
use crate::{mock::Test, EnsureDidOrigin};
144+
use crate::{
145+
mock::{AccountId, DidIdentifier, Test},
146+
EnsureDidOrigin,
147+
};
132148
use frame_support::{assert_ok, traits::EnsureOrigin};
133149

134150
let origin: <Test as frame_system::Config>::RuntimeOrigin =
135-
EnsureDidOrigin::try_successful_origin().expect("Successful origin creation should not fail.");
136-
assert_ok!(EnsureDidOrigin::try_origin(origin));
151+
EnsureDidOrigin::<DidIdentifier, AccountId>::try_successful_origin()
152+
.expect("Successful origin creation should not fail.");
153+
assert_ok!(EnsureDidOrigin::<DidIdentifier, AccountId>::try_origin(origin));
137154
}
138155
}

pallets/pallet-did-lookup/src/benchmarking.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ benchmarks_instance_pallet! {
6666
T::AccountId: From<sr25519::Public> + From<ed25519::Public> + Into<LinkableAccountId> + Into<AccountId32> + From<sp_runtime::AccountId32>,
6767
<T as Config<I>>::DidIdentifier: From<T::AccountId>,
6868
<T as Config<I>>::EnsureOrigin: GenerateBenchmarkOrigin<T::RuntimeOrigin, T::AccountId, <T as Config<I>>::DidIdentifier>,
69+
<T as Config<I>>::AssociateOrigin: GenerateBenchmarkOrigin<T::RuntimeOrigin, T::AccountId, <T as Config<I>>::DidIdentifier>,
6970
<T as Config<I>>::Currency: Mutate<T::AccountId>,
7071
}
7172

@@ -92,7 +93,7 @@ benchmarks_instance_pallet! {
9293
// Add existing connected_acc -> previous_did connection that will be replaced
9394
Pallet::<T, I>::add_association(caller.clone(), previous_did.clone(), linkable_id.clone()).expect("should create previous association");
9495
assert!(ConnectedAccounts::<T, I>::get(&previous_did, linkable_id.clone()).is_some());
95-
let origin = T::EnsureOrigin::generate_origin(caller, did.clone());
96+
let origin = T::AssociateOrigin::generate_origin(caller, did.clone());
9697
let id_arg = linkable_id.clone();
9798
let req = AssociateAccountRequest::Polkadot(connected_acc_id.into(), sig.into());
9899
}: associate_account<T::RuntimeOrigin>(origin, req, expire_at)
@@ -125,7 +126,7 @@ benchmarks_instance_pallet! {
125126
// Add existing connected_acc -> previous_did connection that will be replaced
126127
Pallet::<T, I>::add_association(caller.clone(), previous_did.clone(), linkable_id.clone()).expect("should create previous association");
127128
assert!(ConnectedAccounts::<T, I>::get(&previous_did, linkable_id.clone()).is_some());
128-
let origin = T::EnsureOrigin::generate_origin(caller, did.clone());
129+
let origin = T::AssociateOrigin::generate_origin(caller, did.clone());
129130
let id_arg = linkable_id.clone();
130131
let req = AssociateAccountRequest::Polkadot(connected_acc_id.into(), sig.into());
131132
}: associate_account<T::RuntimeOrigin>(origin, req, expire_at)
@@ -158,7 +159,7 @@ benchmarks_instance_pallet! {
158159
// Add existing connected_acc -> previous_did connection that will be replaced
159160
Pallet::<T, I>::add_association(caller.clone(), previous_did.clone(), linkable_id.clone()).expect("should create previous association");
160161
assert!(ConnectedAccounts::<T, I>::get(&previous_did, linkable_id.clone()).is_some());
161-
let origin = T::EnsureOrigin::generate_origin(caller, did.clone());
162+
let origin = T::AssociateOrigin::generate_origin(caller, did.clone());
162163
let id_arg = linkable_id.clone();
163164
let req = AssociateAccountRequest::Polkadot(connected_acc_id, sig.into());
164165
}: associate_account<T::RuntimeOrigin>(origin, req, expire_at)
@@ -193,7 +194,7 @@ benchmarks_instance_pallet! {
193194
// Add existing connected_acc -> previous_did connection that will be replaced
194195
Pallet::<T, I>::add_association(caller.clone(), previous_did.clone(), eth_account.into()).expect("should create previous association");
195196
assert!(ConnectedAccounts::<T, I>::get(&previous_did, LinkableAccountId::from(eth_account)).is_some());
196-
let origin = T::EnsureOrigin::generate_origin(caller, did.clone());
197+
let origin = T::AssociateOrigin::generate_origin(caller, did.clone());
197198
let req = AssociateAccountRequest::Ethereum(eth_account, sig.into());
198199
}: associate_account<T::RuntimeOrigin>(origin, req, expire_at)
199200
verify {
@@ -213,7 +214,7 @@ benchmarks_instance_pallet! {
213214
// Add existing sender -> previous_did connection that will be replaced
214215
Pallet::<T, I>::add_association(caller.clone(), previous_did.clone(), caller.clone().into()).expect("should create previous association");
215216
assert!(ConnectedAccounts::<T, I>::get(&previous_did, &linkable_id).is_some());
216-
let origin = T::EnsureOrigin::generate_origin(caller, did.clone());
217+
let origin = T::AssociateOrigin::generate_origin(caller, did.clone());
217218
}: _<T::RuntimeOrigin>(origin)
218219
verify {
219220
assert!(ConnectedDids::<T, I>::get(&linkable_id).is_some());

pallets/pallet-did-lookup/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ pub mod pallet {
107107
type RuntimeEvent: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
108108

109109
/// The origin that can associate accounts to itself.
110+
type AssociateOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin, Success = Self::OriginSuccess>;
111+
/// The origin that can do other regular operations, except what
112+
/// `AssociateOrigin` allows.
110113
type EnsureOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin, Success = Self::OriginSuccess>;
111114

112115
/// The information that is returned by the origin check.
@@ -272,7 +275,7 @@ pub mod pallet {
272275
req: AssociateAccountRequest,
273276
expiration: BlockNumberFor<T>,
274277
) -> DispatchResult {
275-
let source = <T as Config<I>>::EnsureOrigin::ensure_origin(origin)?;
278+
let source = <T as Config<I>>::AssociateOrigin::ensure_origin(origin)?;
276279
let did_identifier = source.subject();
277280
let sender = source.sender();
278281

@@ -314,7 +317,7 @@ pub mod pallet {
314317
#[pallet::call_index(1)]
315318
#[pallet::weight(<T as Config<I>>::WeightInfo::associate_sender())]
316319
pub fn associate_sender(origin: OriginFor<T>) -> DispatchResult {
317-
let source = <T as Config<I>>::EnsureOrigin::ensure_origin(origin)?;
320+
let source = <T as Config<I>>::AssociateOrigin::ensure_origin(origin)?;
318321

319322
ensure!(
320323
<<T as Config<I>>::Currency as InspectHold<AccountIdOf<T>>>::can_hold(

pallets/pallet-did-lookup/src/mock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ impl pallet_did_lookup::Config for Test {
134134
type Currency = Balances;
135135
type Deposit = DidLookupDeposit;
136136
type EnsureOrigin = mock_origin::EnsureDoubleOrigin<AccountId, SubjectId>;
137+
type AssociateOrigin = mock_origin::EnsureDoubleOrigin<AccountId, SubjectId>;
137138
type OriginSuccess = mock_origin::DoubleOrigin<AccountId, SubjectId>;
138139
type DidIdentifier = SubjectId;
139140
type WeightInfo = ();

pallets/pallet-dip-consumer/src/mock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ impl pallet_balances::Config for TestRuntime {
8686
}
8787

8888
impl pallet_did_lookup::Config for TestRuntime {
89+
type AssociateOrigin = Self::EnsureOrigin;
8990
type BalanceMigrationManager = ();
9091
type Currency = Balances;
9192
type Deposit = ConstU64<1>;

pallets/pallet-migration/src/mock.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ impl pallet_did_lookup::Config for Test {
283283
type RuntimeHoldReason = RuntimeHoldReason;
284284
type Currency = Balances;
285285
type Deposit = DidLookupDeposit;
286+
type AssociateOrigin = mock_origin::EnsureDoubleOrigin<AccountId, SubjectId>;
286287
type EnsureOrigin = mock_origin::EnsureDoubleOrigin<AccountId, SubjectId>;
287288
type OriginSuccess = mock_origin::DoubleOrigin<AccountId, SubjectId>;
288289
type DidIdentifier = SubjectId;
@@ -329,6 +330,7 @@ parameter_types! {
329330

330331
impl pallet_web3_names::Config for Test {
331332
type BanOrigin = TestBanOrigin;
333+
type ClaimOrigin = TestOwnerOrigin;
332334
type OwnerOrigin = TestOwnerOrigin;
333335
type OriginSuccess = TestOriginSuccess;
334336
type Currency = Balances;

pallets/pallet-web3-names/src/benchmarking.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ benchmarks_instance_pallet! {
7171
T::AccountId: From<sr25519::Public>,
7272
<T as Config<I>>::Web3NameOwner: From<T::AccountId>,
7373
<T as Config<I>>::OwnerOrigin: GenerateBenchmarkOrigin<T::RuntimeOrigin, T::AccountId, <T as Config<I>>::Web3NameOwner>,
74+
<T as Config<I>>::ClaimOrigin: GenerateBenchmarkOrigin<T::RuntimeOrigin, T::AccountId, <T as Config<I>>::Web3NameOwner>,
7475
<T as Config<I>>::BanOrigin: EnsureOrigin<T::RuntimeOrigin>,
7576
<<T as Config<I>>::Web3Name as TryFrom<Vec<u8>>>::Error: Into<Error<T, I>>,
7677
<T as Config<I>>::Currency: Mutate<T::AccountId>,
@@ -82,7 +83,7 @@ benchmarks_instance_pallet! {
8283
let owner: Web3NameOwnerOf<T, I> = account("owner", 0, OWNER_SEED);
8384
let web3_name_input: BoundedVec<u8, <T as Config<I>>::MaxNameLength> = BoundedVec::try_from(<T as Config<I>>::BenchmarkHelper::generate_name_input_with_length(n.saturated_into())).expect("BoundedVec creation should not fail.");
8485
let web3_name_input_clone = web3_name_input.clone();
85-
let origin = <T as Config<I>>::OwnerOrigin::generate_origin(caller.clone(), owner.clone());
86+
let origin = <T as Config<I>>::ClaimOrigin::generate_origin(caller.clone(), owner.clone());
8687

8788
make_free_for_did::<T, I>(&caller);
8889
}: _<T::RuntimeOrigin>(origin, web3_name_input_clone)

pallets/pallet-web3-names/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,10 @@ pub mod pallet {
121121
pub trait Config<I: 'static = ()>: frame_system::Config {
122122
/// The origin allowed to ban names.
123123
type BanOrigin: EnsureOrigin<Self::RuntimeOrigin>;
124-
/// The origin allowed to perform regular operations.
124+
/// The origin allowed to claim a web3name.
125+
type ClaimOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin, Success = Self::OriginSuccess>;
126+
/// The origin allowed to perform all regular operations, except
127+
/// claiming a name, which is authorized by `ClaimOrigin`.
125128
type OwnerOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin, Success = Self::OriginSuccess>;
126129
/// The type of origin after a successful origin check.
127130
type OriginSuccess: CallSources<AccountIdOf<Self>, Web3NameOwnerOf<Self, I>>;
@@ -244,7 +247,7 @@ pub mod pallet {
244247
#[pallet::call_index(0)]
245248
#[pallet::weight(<T as Config<I>>::WeightInfo::claim(name.len().saturated_into()))]
246249
pub fn claim(origin: OriginFor<T>, name: Web3NameInput<T, I>) -> DispatchResult {
247-
let runtime_origin = T::OwnerOrigin::ensure_origin(origin)?;
250+
let runtime_origin = T::ClaimOrigin::ensure_origin(origin)?;
248251
let payer = runtime_origin.sender();
249252
let owner = runtime_origin.subject();
250253

pallets/pallet-web3-names/src/mock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ pub(crate) mod runtime {
180180

181181
impl pallet_web3_names::Config for Test {
182182
type BanOrigin = TestBanOrigin;
183+
type ClaimOrigin = TestOwnerOrigin;
183184
type OwnerOrigin = TestOwnerOrigin;
184185
type OriginSuccess = TestOriginSuccess;
185186
type Currency = Balances;

0 commit comments

Comments
 (0)