-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: prevent DID deletions if there's linked resources #843
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
e12f452
WIP
ntn-x2 05ecd6d
Add more trait implementations
ntn-x2 7a2bc97
Implement runtime logic
ntn-x2 038daf0
Testing mock in progress
ntn-x2 a8b507a
Move the linked resources check to after we check for DID existence
ntn-x2 d5742a8
Mock complete
ntn-x2 5d2ec86
Tests completed
ntn-x2 b107656
Fmt
ntn-x2 3b4784c
Codebase adjustments
ntn-x2 6df985e
Move MockCurrency to its own module
ntn-x2 cd3e9ac
Last changes
ntn-x2 61badeb
Fix block weights
ntn-x2 c919948
Fix weights
ntn-x2 97ef63b
Fix broken cargo doc
ntn-x2 72fc198
Adjust read weight size + unit tests
ntn-x2 8b47e0c
Rename test structs
ntn-x2 f178c82
Upgrade to docstring
ntn-x2 3efbafb
Replace tuple struct with regular one
ntn-x2 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// KILT Blockchain – https://botlabs.org | ||
// Copyright (C) 2019-2024 BOTLabs GmbH | ||
|
||
// The KILT Blockchain 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. | ||
|
||
// The KILT Blockchain 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 this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
// If you feel like getting in touch with us, you can do so at [email protected] | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
use sp_std::marker::PhantomData; | ||
use sp_weights::Weight; | ||
|
||
use crate::{Config, DidIdentifierOf}; | ||
|
||
/// Runtime logic evaluated by the DID pallet upon deleting an existing DID. | ||
pub trait DidDeletionHook<T> | ||
where | ||
T: Config, | ||
{ | ||
/// The statically computed maximum weight the implementation will consume | ||
/// to verify if a DID can be deleted. | ||
const MAX_WEIGHT: Weight; | ||
|
||
/// Return whether the DID can be deleted (`Ok(())`), or not. In case of | ||
/// error, the consumed weight (less than or equal to `MAX_WEIGHT`) is | ||
/// returned. | ||
fn can_delete(did: &DidIdentifierOf<T>) -> Result<(), Weight>; | ||
} | ||
|
||
impl<T> DidDeletionHook<T> for () | ||
where | ||
T: Config, | ||
{ | ||
const MAX_WEIGHT: Weight = Weight::from_parts(0, 0); | ||
|
||
fn can_delete(_did: &DidIdentifierOf<T>) -> Result<(), Weight> { | ||
Ok(()) | ||
} | ||
} | ||
|
||
/// Implementation of [`DidDeletionHook`] that iterates over both | ||
/// components, bailing out early if the first one fails. The `MAX_WEIGHT` is | ||
/// the sum of both components. | ||
pub struct RequireBoth<A, B>(PhantomData<(A, B)>); | ||
|
||
impl<T, A, B> DidDeletionHook<T> for RequireBoth<A, B> | ||
where | ||
T: Config, | ||
A: DidDeletionHook<T>, | ||
B: DidDeletionHook<T>, | ||
{ | ||
const MAX_WEIGHT: Weight = A::MAX_WEIGHT.saturating_add(B::MAX_WEIGHT); | ||
|
||
/// In case of failure, the returned weight is either the weight consumed by | ||
/// the first component, or the sum of the first component's maximum weight | ||
/// and the weight consumed by the second component. | ||
fn can_delete(did: &DidIdentifierOf<T>) -> Result<(), Weight> { | ||
// Bail out early with A's weight if A fails. | ||
A::can_delete(did)?; | ||
// Bail out early with A's max weight + B's if B fails. | ||
B::can_delete(did).map_err(|consumed_weight| A::MAX_WEIGHT.saturating_add(consumed_weight))?; | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// KILT Blockchain – https://botlabs.org | ||
// Copyright (C) 2019-2024 BOTLabs GmbH | ||
|
||
// The KILT Blockchain 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. | ||
|
||
// The KILT Blockchain 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 this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
// If you feel like getting in touch with us, you can do so at [email protected] | ||
|
||
//! Test module for the `RequireBoth` type. It verifies that the type works as | ||
//! expected in case of failure of one of its components. | ||
|
||
use sp_runtime::AccountId32; | ||
use sp_weights::Weight; | ||
|
||
use crate::{ | ||
traits::lifecycle_hooks::{deletion::RequireBoth, mock::TestRuntime, DidDeletionHook}, | ||
DidIdentifierOf, | ||
}; | ||
|
||
struct AlwaysDeny; | ||
|
||
impl DidDeletionHook<TestRuntime> for AlwaysDeny { | ||
const MAX_WEIGHT: Weight = Weight::from_all(10); | ||
|
||
fn can_delete(_did: &DidIdentifierOf<TestRuntime>) -> Result<(), Weight> { | ||
Err(Weight::from_all(5)) | ||
} | ||
} | ||
|
||
struct AlwaysAllow; | ||
|
||
impl DidDeletionHook<TestRuntime> for AlwaysAllow { | ||
const MAX_WEIGHT: Weight = Weight::from_all(20); | ||
|
||
fn can_delete(_did: &DidIdentifierOf<TestRuntime>) -> Result<(), Weight> { | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[test] | ||
fn first_false() { | ||
type TestSubject = RequireBoth<AlwaysDeny, AlwaysAllow>; | ||
|
||
// Max weight is the sum. | ||
assert_eq!(TestSubject::MAX_WEIGHT, Weight::from_all(30)); | ||
// Failure consumes `False`'s weight. | ||
assert_eq!( | ||
TestSubject::can_delete(&AccountId32::new([0u8; 32])), | ||
Err(Weight::from_all(5)) | ||
); | ||
} | ||
|
||
#[test] | ||
fn second_false() { | ||
type TestSubject = RequireBoth<AlwaysAllow, AlwaysDeny>; | ||
|
||
// Max weight is the sum. | ||
assert_eq!(TestSubject::MAX_WEIGHT, Weight::from_all(30)); | ||
// Failure consumes the sum of `True`'s max weight and `False`'s weight. | ||
assert_eq!( | ||
TestSubject::can_delete(&AccountId32::new([0u8; 32])), | ||
Err(Weight::from_all(25)) | ||
); | ||
} | ||
|
||
#[test] | ||
fn both_true() { | ||
type TestSubject = RequireBoth<AlwaysAllow, AlwaysAllow>; | ||
|
||
// Max weight is the sum. | ||
assert_eq!(TestSubject::MAX_WEIGHT, Weight::from_all(40)); | ||
// Overall result is `Ok`. | ||
assert_eq!(TestSubject::can_delete(&AccountId32::new([0u8; 32])), Ok(())); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
// KILT Blockchain – https://botlabs.org | ||
// Copyright (C) 2019-2024 BOTLabs GmbH | ||
|
||
// The KILT Blockchain 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. | ||
|
||
// The KILT Blockchain 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 this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
// If you feel like getting in touch with us, you can do so at [email protected] | ||
|
||
use frame_support::{construct_runtime, parameter_types}; | ||
use frame_system::{mocking::MockBlock, EnsureSigned}; | ||
use kilt_support::mock::MockCurrency; | ||
use parity_scale_codec::{Decode, Encode}; | ||
use scale_info::TypeInfo; | ||
use sp_core::{ConstU32, ConstU64, H256}; | ||
use sp_runtime::{ | ||
traits::{BlakeTwo256, IdentityLookup}, | ||
AccountId32, | ||
}; | ||
|
||
use crate::{ | ||
Config, DeriveDidCallAuthorizationVerificationKeyRelationship, DeriveDidCallKeyRelationshipResult, | ||
DidVerificationKeyRelationship, | ||
}; | ||
|
||
construct_runtime!( | ||
pub enum TestRuntime | ||
{ | ||
System: frame_system, | ||
Did: crate, | ||
} | ||
); | ||
|
||
impl frame_system::Config for TestRuntime { | ||
type AccountData = (); | ||
type AccountId = AccountId32; | ||
type BaseCallFilter = (); | ||
type Block = MockBlock<TestRuntime>; | ||
type BlockHashCount = ConstU64<1>; | ||
type BlockLength = (); | ||
type BlockWeights = (); | ||
type DbWeight = (); | ||
type Hash = H256; | ||
type Hashing = BlakeTwo256; | ||
type Lookup = IdentityLookup<Self::AccountId>; | ||
type MaxConsumers = ConstU32<1>; | ||
type Nonce = u64; | ||
type OnKilledAccount = (); | ||
type OnNewAccount = (); | ||
type OnSetCode = (); | ||
type PalletInfo = PalletInfo; | ||
type RuntimeEvent = (); | ||
type RuntimeOrigin = RuntimeOrigin; | ||
type RuntimeCall = RuntimeCall; | ||
type RuntimeTask = (); | ||
type SS58Prefix = (); | ||
type SystemWeightInfo = (); | ||
type Version = (); | ||
} | ||
|
||
parameter_types! { | ||
#[derive(TypeInfo, Debug, PartialEq, Eq, Clone, Encode, Decode)] | ||
pub const MaxNewKeyAgreementKeys: u32 = 1; | ||
#[derive(TypeInfo, Debug, PartialEq, Eq, Clone, Encode, Decode)] | ||
pub const MaxTotalKeyAgreementKeys: u32 = 1; | ||
} | ||
|
||
impl DeriveDidCallAuthorizationVerificationKeyRelationship for RuntimeCall { | ||
fn derive_verification_key_relationship(&self) -> DeriveDidCallKeyRelationshipResult { | ||
Ok(DidVerificationKeyRelationship::Authentication) | ||
} | ||
|
||
#[cfg(feature = "runtime-benchmarks")] | ||
fn get_call_for_did_call_benchmark() -> Self { | ||
Self::System(frame_system::Call::remark { | ||
remark: b"test".to_vec(), | ||
}) | ||
} | ||
} | ||
|
||
impl Config for TestRuntime { | ||
type BalanceMigrationManager = (); | ||
type BaseDeposit = ConstU64<1>; | ||
type Currency = MockCurrency<u64, RuntimeHoldReason>; | ||
type DidIdentifier = AccountId32; | ||
type DidLifecycleHooks = (); | ||
type EnsureOrigin = EnsureSigned<AccountId32>; | ||
type Fee = ConstU64<1>; | ||
type FeeCollector = (); | ||
type KeyDeposit = ConstU64<1>; | ||
type MaxBlocksTxValidity = ConstU64<1>; | ||
type MaxNewKeyAgreementKeys = MaxNewKeyAgreementKeys; | ||
type MaxNumberOfServicesPerDid = ConstU32<1>; | ||
type MaxNumberOfTypesPerService = ConstU32<1>; | ||
type MaxNumberOfUrlsPerService = ConstU32<1>; | ||
type MaxPublicKeysPerDid = ConstU32<1>; | ||
type MaxServiceIdLength = ConstU32<1>; | ||
type MaxServiceTypeLength = ConstU32<1>; | ||
type MaxServiceUrlLength = ConstU32<1>; | ||
type MaxTotalKeyAgreementKeys = MaxTotalKeyAgreementKeys; | ||
type OriginSuccess = AccountId32; | ||
type RuntimeCall = RuntimeCall; | ||
type RuntimeEvent = (); | ||
type RuntimeHoldReason = RuntimeHoldReason; | ||
type RuntimeOrigin = RuntimeOrigin; | ||
type ServiceEndpointDeposit = ConstU64<1>; | ||
type WeightInfo = (); | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is feasible in substrate, but I'd much rather raise errors in the hook and have them propagated, so that we can inform callers on which deletion conditions have not been met.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see this approach hitting a wall though if we can't come up with a way to define errors that are not already defined in any of the pallets. Substrate seems to be somewhat limited in that regard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing we can do is return
u8
oru16
error codes, which are returned by the runtime hook. Then we would need to come up with a scheme that would be able to encode in 8 or 16 bits all the different "components" that are linked to the DID, to be able to return an aggregate error of all the pending stuff instead of returning the error of the first item that was encountered.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we will add runtime APIs to ease deletion, I would say that a dry run of this extrinsic + a call to the API to return the list of call is sufficient to solve this issue. I would not over-complicate the runtime code.