diff --git a/Cargo.lock b/Cargo.lock index 5609faeca2..f49eee1cb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -362,7 +362,6 @@ name = "attestation" version = "1.5.0" dependencies = [ "ctype", - "delegation", "frame-benchmarking", "frame-support", "frame-system", @@ -370,7 +369,6 @@ dependencies = [ "log", "pallet-balances", "parity-scale-codec", - "runtime-common", "scale-info", "serde", "sp-core", @@ -1375,7 +1373,6 @@ dependencies = [ "log", "pallet-balances", "parity-scale-codec", - "runtime-common", "scale-info", "serde", "sp-core", @@ -1842,6 +1839,7 @@ dependencies = [ name = "delegation" version = "1.5.0" dependencies = [ + "attestation", "bitflags", "ctype", "env_logger 0.8.4", @@ -1852,7 +1850,6 @@ dependencies = [ "log", "pallet-balances", "parity-scale-codec", - "runtime-common", "scale-info", "serde", "sp-core", @@ -8556,6 +8553,7 @@ dependencies = [ name = "runtime-common" version = "1.5.0" dependencies = [ + "attestation", "frame-support", "frame-system", "pallet-authorship", diff --git a/pallets/attestation/Cargo.toml b/pallets/attestation/Cargo.toml index f282aeb1b2..34d3cf5b40 100644 --- a/pallets/attestation/Cargo.toml +++ b/pallets/attestation/Cargo.toml @@ -14,8 +14,7 @@ substrate-wasm-builder-runner = {version = "3.0.0"} [dev-dependencies] ctype = {features = ["mock"], path = "../ctype"} -delegation = {features = ["mock"], path = "../delegation"} -runtime-common = {default-features = false, path = "../../runtimes/common"} +kilt-support = {features = ["mock"], path = "../../support"} pallet-balances = {branch = "polkadot-v0.9.17", default-features = false, git = "https://github.com/paritytech/substrate"} serde = {version = "1.0.132"} @@ -31,9 +30,7 @@ serde = {optional = true, version = "1.0.132"} # Internal dependencies ctype = {default-features = false, path = "../ctype"} -delegation = {default-features = false, path = "../delegation"} kilt-support = {default-features = false, path = "../../support"} -runtime-common = {default-features = false, optional = true, path = "../../runtimes/common"} #External dependencies frame-benchmarking = {branch = "polkadot-v0.9.17", default-features = false, git = "https://github.com/paritytech/substrate", optional = true} @@ -49,7 +46,6 @@ sp-std = {branch = "polkadot-v0.9.17", default-features = false, git = "https:// [features] default = ["std"] mock = [ - "runtime-common", "pallet-balances", "serde", "sp-core", @@ -57,19 +53,17 @@ mock = [ "sp-keystore", ] runtime-benchmarks = [ - "delegation/runtime-benchmarks", "frame-benchmarking", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kilt-support/runtime-benchmarks", "sp-core", ] std = [ "codec/std", "ctype/std", - "delegation/std", "frame-support/std", "frame-system/std", - "runtime-common/std", "kilt-support/std", "log/std", "pallet-balances/std", diff --git a/pallets/attestation/src/access_control.rs b/pallets/attestation/src/access_control.rs new file mode 100644 index 0000000000..a94d79502e --- /dev/null +++ b/pallets/attestation/src/access_control.rs @@ -0,0 +1,105 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2022 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 . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use frame_support::dispatch::Weight; +use sp_runtime::DispatchError; + +/// Allow for more complex schemes on who can attest, revoke and remove. +pub trait AttestationAccessControl { + /// Decides whether the account is allowed to attest with the given + /// information provided by the sender (&self). + fn can_attest(&self, who: &AttesterId, ctype: &Ctype, claim: &ClaimHash) -> Result; + + /// Decides whether the account is allowed to revoke the attestation with + /// the `authorization_id` and the access information provided by the sender + /// (&self). + fn can_revoke( + &self, + who: &AttesterId, + ctype: &Ctype, + claim: &ClaimHash, + authorization_id: &AuthorizationId, + ) -> Result; + + /// Decides whether the account is allowed to remove the attestation with + /// the `authorization_id` and the access information provided by the sender + /// (&self). + fn can_remove( + &self, + who: &AttesterId, + ctype: &Ctype, + claim: &ClaimHash, + authorization_id: &AuthorizationId, + ) -> Result; + + /// The authorization ID that the sender provided. This will be used for new + /// attestations. + /// + /// NOTE: This method must not read storage or do any heavy computation + /// since it's not covered by the weight returned by `self.weight()`. + fn authorization_id(&self) -> AuthorizationId; + + /// The worst-case weight of `can_attest`. + fn can_attest_weight(&self) -> Weight; + + /// The worst-case weight of `can_revoke`. + fn can_revoke_weight(&self) -> Weight; + + /// The worst-case weight of `can_remove`. + fn can_remove_weight(&self) -> Weight; +} + +impl + AttestationAccessControl for () +where + AuthorizationId: Default, +{ + fn can_attest(&self, _who: &AttesterId, _ctype: &Ctype, _claim: &ClaimHash) -> Result { + Err(DispatchError::Other("Unimplemented")) + } + fn can_revoke( + &self, + _who: &AttesterId, + _ctype: &Ctype, + _claim: &ClaimHash, + _authorization_id: &AuthorizationId, + ) -> Result { + Err(DispatchError::Other("Unimplemented")) + } + fn can_remove( + &self, + _who: &AttesterId, + _ctype: &Ctype, + _claim: &ClaimHash, + _authorization_id: &AuthorizationId, + ) -> Result { + Err(DispatchError::Other("Unimplemented")) + } + fn authorization_id(&self) -> AuthorizationId { + Default::default() + } + fn can_attest_weight(&self) -> Weight { + 0 + } + fn can_revoke_weight(&self) -> Weight { + 0 + } + fn can_remove_weight(&self) -> Weight { + 0 + } +} diff --git a/pallets/attestation/src/attestations.rs b/pallets/attestation/src/attestations.rs index 5cb2dc7cd2..677d10377f 100644 --- a/pallets/attestation/src/attestations.rs +++ b/pallets/attestation/src/attestations.rs @@ -18,11 +18,10 @@ use codec::{Decode, Encode, MaxEncodedLen}; use ctype::CtypeHashOf; -use delegation::DelegationNodeIdOf; use kilt_support::deposit::Deposit; use scale_info::TypeInfo; -use crate::{AccountIdOf, AttesterOf, BalanceOf, Config}; +use crate::{AccountIdOf, AttesterOf, AuthorizationIdOf, BalanceOf, Config}; /// An on-chain attestation written by an attester. #[derive(Clone, Debug, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen)] @@ -35,10 +34,65 @@ pub struct AttestationDetails { pub attester: AttesterOf, /// \[OPTIONAL\] The ID of the delegation node used to authorize the /// attester. - pub delegation_id: Option>, + pub authorization_id: Option>, /// The flag indicating whether the attestation has been revoked or not. pub revoked: bool, /// The deposit that was taken to incentivise fair use of the on chain /// storage. pub deposit: Deposit, BalanceOf>, } + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::*; + + /// Old Attestation + #[derive(Clone, Debug, Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen)] + #[scale_info(skip_type_params(T))] + #[codec(mel_bound())] + pub struct OldAttestationDetails { + /// The hash of the CType used for this attestation. + pub ctype_hash: CtypeHashOf, + /// The ID of the attester. + pub attester: AttesterOf, + /// \[OPTIONAL\] The ID of the delegation node used to authorize the + /// attester. + pub delegation_id: Option<[u8; 32]>, + /// The flag indicating whether the attestation has been revoked or not. + pub revoked: bool, + /// The deposit that was taken to incentivise fair use of the on chain + /// storage. + pub deposit: Deposit, BalanceOf>, + } + + #[test] + fn test_no_need_to_migrate_if_none() { + let old = OldAttestationDetails:: { + ctype_hash: claim_hash_from_seed(CLAIM_HASH_SEED_01), + attester: sr25519_did_from_seed(&ALICE_SEED), + delegation_id: None, + revoked: true, + deposit: Deposit { + owner: ACCOUNT_00, + amount: ATTESTATION_DEPOSIT, + }, + }; + let encoded = old.encode(); + + let new = AttestationDetails::::decode(&mut &encoded[..]); + assert_eq!( + new, + Ok(AttestationDetails:: { + ctype_hash: claim_hash_from_seed(CLAIM_HASH_SEED_01), + attester: sr25519_did_from_seed(&ALICE_SEED), + authorization_id: None, + revoked: true, + deposit: Deposit { + owner: ACCOUNT_00, + amount: ATTESTATION_DEPOSIT, + }, + }) + ); + } +} diff --git a/pallets/attestation/src/benchmarking.rs b/pallets/attestation/src/benchmarking.rs index 2bfe53331c..8b4c123c74 100644 --- a/pallets/attestation/src/benchmarking.rs +++ b/pallets/attestation/src/benchmarking.rs @@ -19,49 +19,39 @@ use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite}; use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; -use sp_core::sr25519; use sp_runtime::traits::Hash; -use sp_std::num::NonZeroU32; -use delegation::{benchmarking::setup_delegations, Config as DelegationConfig, Permissions}; -use kilt_support::{signature::VerifySignature, traits::GenerateBenchmarkOrigin}; +use kilt_support::traits::GenerateBenchmarkOrigin; use crate::*; -const ONE_CHILD_PER_LEVEL: Option = NonZeroU32::new(1); const SEED: u32 = 0; benchmarks! { where_clause { where T: core::fmt::Debug, - T::DelegationNodeId: From, - T::CtypeCreatorId: From, - T::DelegationEntityId: From, - <::DelegationSignatureVerification as VerifySignature>::Signature: From<( - T::DelegationEntityId, - <::DelegationSignatureVerification as VerifySignature>::Payload, - )>, - ::EnsureOrigin: GenerateBenchmarkOrigin, - ::EnsureOrigin: GenerateBenchmarkOrigin, + ::EnsureOrigin: GenerateBenchmarkOrigin, + T: ctype::Config, } add { let sender: T::AccountId = account("sender", 0, SEED); + let attester: T::AttesterId = account("attester", 0, SEED); let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - let (_, _, delegate_public, delegation_id) = setup_delegations::(1, ONE_CHILD_PER_LEVEL.expect(">0"), Permissions::ATTEST)?; - let delegate_acc: T::DelegationEntityId = delegate_public.into(); + + ctype::Ctypes::::insert(&ctype_hash, attester.clone()); ::Currency::make_free_balance_be(&sender, ::Deposit::get() + ::Deposit::get()); - let origin = ::EnsureOrigin::generate_origin(sender.clone(), delegate_acc.clone()); - }: _(origin, claim_hash, ctype_hash, Some(delegation_id)) + let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); + }: _(origin, claim_hash, ctype_hash, None) verify { assert!(Attestations::::contains_key(claim_hash)); assert_eq!(Pallet::::attestations(claim_hash), Some(AttestationDetails { ctype_hash, - attester: delegate_acc, - delegation_id: Some(delegation_id), + attester, + authorization_id: None, revoked: false, deposit: kilt_support::deposit::Deposit { owner: sender, @@ -71,29 +61,23 @@ benchmarks! { } revoke { - let d in 1 .. T::MaxParentChecks::get(); - let sender: T::AccountId = account("sender", 0, SEED); + let attester: T::AttesterId = account("attester", 0, SEED); let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - let (root_public, _, delegate_public, delegation_id) = setup_delegations::(d, ONE_CHILD_PER_LEVEL.expect(">0"), Permissions::ATTEST | Permissions::DELEGATE)?; - let root_acc: T::DelegationEntityId = root_public.into(); - let delegate_acc: T::DelegationEntityId = delegate_public.into(); + ctype::Ctypes::::insert(&ctype_hash, attester.clone()); ::Currency::make_free_balance_be(&sender, ::Deposit::get() + ::Deposit::get()); - // attest with leaf account - let origin = ::EnsureOrigin::generate_origin(sender.clone(), delegate_acc.clone()); - Pallet::::add(origin, claim_hash, ctype_hash, Some(delegation_id))?; - // revoke with root account, s.t. delegation tree needs to be traversed - let origin = ::EnsureOrigin::generate_origin(sender.clone(), root_acc); - }: _(origin, claim_hash, d) + let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); + Pallet::::add(origin.clone(), claim_hash, ctype_hash, None)?; + }: _(origin, claim_hash, None) verify { assert!(Attestations::::contains_key(claim_hash)); assert_eq!(Attestations::::get(claim_hash), Some(AttestationDetails { ctype_hash, - attester: delegate_acc, - delegation_id: Some(delegation_id), + attester, + authorization_id: None, revoked: true, deposit: kilt_support::deposit::Deposit { owner: sender, @@ -103,41 +87,34 @@ benchmarks! { } remove { - let d in 1 .. T::MaxParentChecks::get(); - + let attester: T::AttesterId = account("attester", 0, SEED); + let sender: T::AccountId = account("sender", 0, SEED); let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - let sender: T::AccountId = account("sender", 0, SEED); - let (root_public, _, delegate_public, delegation_id) = setup_delegations::(d, ONE_CHILD_PER_LEVEL.expect(">0"), Permissions::ATTEST | Permissions::DELEGATE)?; - let root_acc: T::DelegationEntityId = root_public.into(); - let delegate_acc: T::DelegationEntityId = delegate_public.into(); + ctype::Ctypes::::insert(&ctype_hash, attester.clone()); ::Currency::make_free_balance_be(&sender, ::Deposit::get() + ::Deposit::get()); - // attest with leaf account - let origin = ::EnsureOrigin::generate_origin(sender.clone(), delegate_acc); - Pallet::::add(origin, claim_hash, ctype_hash, Some(delegation_id))?; - // revoke with root account, s.t. delegation tree needs to be traversed - let origin = ::EnsureOrigin::generate_origin(sender, root_acc); - }: _(origin, claim_hash, d) + let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); + Pallet::::add(origin, claim_hash, ctype_hash, None)?; + let origin = ::EnsureOrigin::generate_origin(sender, attester); + }: _(origin, claim_hash, None) verify { assert!(!Attestations::::contains_key(claim_hash)); } reclaim_deposit { + let attester: T::AttesterId = account("attester", 0, SEED); + let sender: T::AccountId = account("sender", 0, SEED); let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - let sender: T::AccountId = account("sender", 0, SEED); - let (root_public, _, delegate_public, delegation_id) = setup_delegations::(1, ONE_CHILD_PER_LEVEL.expect(">0"), Permissions::ATTEST | Permissions::DELEGATE)?; - let root_acc: T::DelegationEntityId = root_public.into(); - let delegate_acc: T::DelegationEntityId = delegate_public.into(); + ctype::Ctypes::::insert(&ctype_hash, attester.clone()); ::Currency::make_free_balance_be(&sender, ::Deposit::get() + ::Deposit::get()); - // attest with leaf account - let origin = ::EnsureOrigin::generate_origin(sender.clone(), delegate_acc); - Pallet::::add(origin, claim_hash, ctype_hash, Some(delegation_id))?; - // revoke with root account, s.t. delegation tree needs to be traversed + let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester); + Pallet::::add(origin, claim_hash, ctype_hash, None)?; + // revoke with root account let origin = RawOrigin::Signed(sender); }: _(origin, claim_hash) verify { diff --git a/pallets/attestation/src/default_weights.rs b/pallets/attestation/src/default_weights.rs index a38c1ea153..10634e2a98 100644 --- a/pallets/attestation/src/default_weights.rs +++ b/pallets/attestation/src/default_weights.rs @@ -47,8 +47,8 @@ use sp_std::marker::PhantomData; /// Weight functions needed for attestation. pub trait WeightInfo { fn add() -> Weight; - fn revoke(d: u32, ) -> Weight; - fn remove(d: u32, ) -> Weight; + fn revoke() -> Weight; + fn remove() -> Weight; fn reclaim_deposit() -> Weight; } @@ -60,20 +60,20 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - fn revoke(d: u32, ) -> Weight { + fn revoke() -> Weight { (37_029_000_u64) // Standard Error: 44_000 - .saturating_add((6_325_000_u64).saturating_mul(d as Weight)) + .saturating_add(6_325_000_u64) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d as Weight))) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - fn remove(d: u32, ) -> Weight { + fn remove() -> Weight { (64_058_000_u64) // Standard Error: 44_000 - .saturating_add((6_317_000_u64).saturating_mul(d as Weight)) + .saturating_add(6_317_000_u64) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d as Weight))) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } fn reclaim_deposit() -> Weight { @@ -90,20 +90,20 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - fn revoke(d: u32, ) -> Weight { + fn revoke() -> Weight { (37_029_000_u64) // Standard Error: 44_000 - .saturating_add((6_325_000_u64).saturating_mul(d as Weight)) + .saturating_add(6_325_000_u64) .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(d as Weight))) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - fn remove(d: u32, ) -> Weight { + fn remove() -> Weight { (64_058_000_u64) // Standard Error: 44_000 - .saturating_add((6_317_000_u64).saturating_mul(d as Weight)) + .saturating_add(6_317_000_u64) .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(d as Weight))) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } fn reclaim_deposit() -> Weight { diff --git a/pallets/attestation/src/lib.rs b/pallets/attestation/src/lib.rs index a1e8aba421..707a508b0c 100644 --- a/pallets/attestation/src/lib.rs +++ b/pallets/attestation/src/lib.rs @@ -82,32 +82,39 @@ pub mod mock; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +mod access_control; #[cfg(test)] mod tests; -pub use crate::{attestations::AttestationDetails, default_weights::WeightInfo, pallet::*}; +pub use crate::{ + access_control::AttestationAccessControl, attestations::AttestationDetails, default_weights::WeightInfo, pallet::*, +}; #[frame_support::pallet] pub mod pallet { use super::*; - use ctype::CtypeHashOf; - use delegation::DelegationNodeIdOf; use frame_support::{ + dispatch::{DispatchResult, DispatchResultWithPostInfo}, pallet_prelude::*, traits::{Currency, Get, ReservableCurrency, StorageVersion}, - BoundedVec, }; use frame_system::pallet_prelude::*; + use sp_runtime::DispatchError; + + use ctype::CtypeHashOf; use kilt_support::{deposit::Deposit, traits::CallSources}; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); /// Type of a claim hash. - pub(crate) type ClaimHashOf = ::Hash; + pub type ClaimHashOf = ::Hash; /// Type of an attester identifier. - pub(crate) type AttesterOf = delegation::DelegatorIdOf; + pub(crate) type AttesterOf = ::AttesterId; + + /// Authorization id type + pub(crate) type AuthorizationIdOf = ::AuthorizationId; pub(crate) type AccountIdOf = ::AccountId; @@ -116,7 +123,7 @@ pub mod pallet { pub(crate) type CurrencyOf = ::Currency; #[pallet::config] - pub trait Config: frame_system::Config + ctype::Config + delegation::Config { + pub trait Config: frame_system::Config + ctype::Config { type EnsureOrigin: EnsureOrigin< Success = ::OriginSuccess, ::Origin, @@ -136,6 +143,13 @@ pub mod pallet { /// the same delegation. #[pallet::constant] type MaxDelegatedAttestations: Get; + + type AttesterId: Parameter + MaxEncodedLen; + + type AuthorizationId: Parameter + MaxEncodedLen; + + type AccessControl: Parameter + + AttestationAccessControl, ClaimHashOf>; } #[pallet::pallet] @@ -157,13 +171,9 @@ pub mod pallet { /// /// It maps from a delegation ID to a vector of claim hashes. #[pallet::storage] - #[pallet::getter(fn delegated_attestations)] - pub type DelegatedAttestations = StorageMap< - _, - Blake2_128Concat, - DelegationNodeIdOf, - BoundedVec, ::MaxDelegatedAttestations>, - >; + #[pallet::getter(fn external_attestations)] + pub type ExternalAttestations = + StorageDoubleMap<_, Twox64Concat, AuthorizationIdOf, Blake2_128Concat, ClaimHashOf, bool, ValueQuery>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -174,7 +184,7 @@ pub mod pallet { AttesterOf, ClaimHashOf, CtypeHashOf, - Option>, + Option>, ), /// An attestation has been revoked. /// \[account id, claim hash\] @@ -199,15 +209,6 @@ pub mod pallet { /// The attestation CType does not match the CType specified in the /// delegation hierarchy root. CTypeMismatch, - /// The delegation node does not include the permission to create new - /// attestations. Only when the revoker is not the original attester. - DelegationUnauthorizedToAttest, - /// The delegation node has already been revoked. - /// Only when the revoker is not the original attester. - DelegationRevoked, - /// The delegation node owner is different than the attester. - /// Only when the revoker is not the original attester. - NotDelegatedToAttester, /// The call origin is not authorized to change the attestation. Unauthorized, /// The maximum number of delegated attestations has already been @@ -239,12 +240,15 @@ pub mod pallet { /// DelegatedAttestations /// - Writes: Attestations, (DelegatedAttestations) /// # - #[pallet::weight(::WeightInfo::add())] + #[pallet::weight( + ::WeightInfo::add() + .saturating_add(authorization.as_ref().map(|ac| ac.can_attest_weight()).unwrap_or(0)) + )] pub fn add( origin: OriginFor, claim_hash: ClaimHashOf, ctype_hash: CtypeHashOf, - delegation_id: Option>, + authorization: Option, ) -> DispatchResult { let source = ::EnsureOrigin::ensure_origin(origin)?; let payer = source.sender(); @@ -261,34 +265,11 @@ pub mod pallet { ); // Check for validity of the delegation node if specified. - let delegation_record = if let Some(delegation_id) = delegation_id { - let delegation = >::get(delegation_id) - .ok_or(delegation::Error::::DelegationNotFound)?; - - ensure!(!delegation.details.revoked, Error::::DelegationRevoked); - - ensure!(delegation.details.owner == who, Error::::NotDelegatedToAttester); - - ensure!( - (delegation.details.permissions & delegation::Permissions::ATTEST) - == delegation::Permissions::ATTEST, - Error::::DelegationUnauthorizedToAttest - ); - - // Check if the CType of the delegation is matching the CType of the attestation - let root = >::get(delegation.hierarchy_root_id) - .ok_or(delegation::Error::::HierarchyNotFound)?; - ensure!(root.ctype_hash == ctype_hash, Error::::CTypeMismatch); - - // If the attestation is based on a delegation, store separately - let mut delegated_attestations = >::get(delegation_id).unwrap_or_default(); - delegated_attestations - .try_push(claim_hash) - .map_err(|_| Error::::MaxDelegatedAttestationsExceeded)?; - Some((delegation_id, delegated_attestations)) - } else { - None - }; + authorization + .as_ref() + .map(|ac| ac.can_attest(&who, &ctype_hash, &claim_hash)) + .transpose()?; + let authorization_id = authorization.as_ref().map(|ac| ac.authorization_id()); let deposit = Pallet::::reserve_deposit(payer, deposit_amount)?; @@ -296,23 +277,22 @@ pub mod pallet { log::debug!("insert Attestation"); - // write delegation record, if any - if let Some((id, delegated_attestation)) = delegation_record { - >::insert(id, delegated_attestation); - } - - >::insert( + Attestations::::insert( &claim_hash, AttestationDetails { ctype_hash, attester: who.clone(), - delegation_id, + authorization_id: authorization_id.clone(), revoked: false, deposit, }, ); + if let Some(authorization_id) = &authorization_id { + ExternalAttestations::::insert(authorization_id, claim_hash, true); + } + + Self::deposit_event(Event::AttestationCreated(who, claim_hash, ctype_hash, authorization_id)); - Self::deposit_event(Event::AttestationCreated(who, claim_hash, ctype_hash, delegation_id)); Ok(()) } @@ -333,11 +313,14 @@ pub mod pallet { /// - Reads per delegation step P: delegation::Delegations /// - Writes: Attestations, DelegatedAttestations /// # - #[pallet::weight(::WeightInfo::revoke(*max_parent_checks))] + #[pallet::weight( + ::WeightInfo::revoke() + .saturating_add(authorization.as_ref().map(|ac| ac.can_revoke_weight()).unwrap_or(0)) + )] pub fn revoke( origin: OriginFor, claim_hash: ClaimHashOf, - max_parent_checks: u32, + authorization: Option, ) -> DispatchResultWithPostInfo { let source = ::EnsureOrigin::ensure_origin(origin)?; let who = source.subject(); @@ -346,16 +329,20 @@ pub mod pallet { ensure!(!attestation.revoked, Error::::AlreadyRevoked); - let delegation_depth = if attestation.attester != who { - Self::verify_delegated_access(&who, &attestation, max_parent_checks)? - } else { - 0 - }; + if attestation.attester != who { + let attestation_auth_id = attestation.authorization_id.as_ref().ok_or(Error::::Unauthorized)?; + authorization.ok_or(Error::::Unauthorized)?.can_revoke( + &who, + &attestation.ctype_hash, + &claim_hash, + attestation_auth_id, + )?; + } // *** No Fail beyond this point *** log::debug!("revoking Attestation"); - >::insert( + Attestations::::insert( &claim_hash, AttestationDetails { revoked: true, @@ -365,7 +352,7 @@ pub mod pallet { Self::deposit_event(Event::AttestationRevoked(who, claim_hash)); - Ok(Some(::WeightInfo::revoke(delegation_depth)).into()) + Ok(Some(::WeightInfo::revoke()).into()) } /// Remove an attestation. @@ -385,22 +372,29 @@ pub mod pallet { /// - Reads per delegation step P: delegation::Delegations /// - Writes: Attestations, DelegatedAttestations /// # - #[pallet::weight(::WeightInfo::remove(*max_parent_checks))] + #[pallet::weight( + ::WeightInfo::remove() + .saturating_add(authorization.as_ref().map(|ac| ac.can_remove_weight()).unwrap_or(0)) + )] pub fn remove( origin: OriginFor, claim_hash: ClaimHashOf, - max_parent_checks: u32, + authorization: Option, ) -> DispatchResultWithPostInfo { let source = ::EnsureOrigin::ensure_origin(origin)?; let who = source.subject(); let attestation = Attestations::::get(&claim_hash).ok_or(Error::::AttestationNotFound)?; - let delegation_depth = if attestation.attester != who { - Self::verify_delegated_access(&who, &attestation, max_parent_checks)? - } else { - 0 - }; + if attestation.attester != who { + let attestation_auth_id = attestation.authorization_id.as_ref().ok_or(Error::::Unauthorized)?; + authorization.ok_or(Error::::Unauthorized)?.can_remove( + &who, + &attestation.ctype_hash, + &claim_hash, + attestation_auth_id, + )?; + } // *** No Fail beyond this point *** @@ -409,7 +403,7 @@ pub mod pallet { Self::remove_attestation(attestation, claim_hash); Self::deposit_event(Event::AttestationRemoved(who, claim_hash)); - Ok(Some(::WeightInfo::remove(delegation_depth)).into()) + Ok(Some(::WeightInfo::remove()).into()) } /// Reclaim a storage deposit by removing an attestation @@ -440,28 +434,6 @@ pub mod pallet { } impl Pallet { - /// Check the delegation tree if the attester is authorized to access - /// the attestation. - fn verify_delegated_access( - attester: &AttesterOf, - attestation: &AttestationDetails, - max_parent_checks: u32, - ) -> Result { - // if there is no delegation id, access to this attestation wasn't delegated to - // anyone. - let delegation_id = attestation.delegation_id.ok_or(Error::::Unauthorized)?; - ensure!( - max_parent_checks <= T::MaxParentChecks::get(), - delegation::Error::::MaxParentChecksTooLarge - ); - // Check whether the sender of the revocation controls the delegation node and - // that the delegation has not been revoked - let (is_delegating, delegation_depth) = - >::is_delegating(attester, &delegation_id, max_parent_checks)?; - ensure!(is_delegating, Error::::Unauthorized); - Ok(delegation_depth) - } - /// Reserve the deposit and record the deposit on chain. /// /// Fails if the `payer` has a balance less than deposit. @@ -480,12 +452,8 @@ pub mod pallet { fn remove_attestation(attestation: AttestationDetails, claim_hash: ClaimHashOf) { kilt_support::free_deposit::, CurrencyOf>(&attestation.deposit); Attestations::::remove(&claim_hash); - if let Some(delegation_id) = attestation.delegation_id { - DelegatedAttestations::::mutate(&delegation_id, |maybe_attestations| { - if let Some(attestations) = maybe_attestations.as_mut() { - attestations.retain(|&elem| elem != claim_hash); - } - }); + if let Some(authorization_id) = &attestation.authorization_id { + ExternalAttestations::::remove(authorization_id, claim_hash); } } } diff --git a/pallets/attestation/src/mock.rs b/pallets/attestation/src/mock.rs index 3eb90fdbb3..51665c2b28 100644 --- a/pallets/attestation/src/mock.rs +++ b/pallets/attestation/src/mock.rs @@ -23,13 +23,19 @@ //! other tests. Internal functions/structs can only be used in attestation //! tests. +use codec::{Decode, Encode}; +use frame_support::{dispatch::Weight, traits::Get}; +use scale_info::TypeInfo; +use sp_core::H256; +use sp_runtime::DispatchError; + use ctype::CtypeHashOf; -use delegation::DelegationNodeIdOf; -use frame_support::traits::Get; use kilt_support::deposit::Deposit; -use sp_core::H256; -use crate::{AccountIdOf, AttestationDetails, AttesterOf, BalanceOf, ClaimHashOf, Config}; +use crate::{ + pallet::AuthorizationIdOf, AccountIdOf, AttestationAccessControl, AttestationDetails, AttesterOf, BalanceOf, + ClaimHashOf, Config, +}; #[cfg(test)] pub use crate::mock::runtime::*; @@ -37,7 +43,7 @@ pub use crate::mock::runtime::*; pub struct AttestationCreationDetails { pub claim_hash: ClaimHashOf, pub ctype_hash: CtypeHashOf, - pub delegation_id: Option>, + pub authorization_id: Option>, } pub fn generate_base_attestation_creation_details( @@ -47,21 +53,7 @@ pub fn generate_base_attestation_creation_details( AttestationCreationDetails { claim_hash, ctype_hash: attestation.ctype_hash, - delegation_id: attestation.delegation_id, - } -} - -pub struct AttestationRevocationDetails { - pub claim_hash: ClaimHashOf, - pub max_parent_checks: u32, -} - -pub fn generate_base_attestation_revocation_details( - claim_hash: ClaimHashOf, -) -> AttestationRevocationDetails { - AttestationRevocationDetails { - claim_hash, - max_parent_checks: 0u32, + authorization_id: attestation.authorization_id, } } @@ -72,7 +64,7 @@ where { AttestationDetails { attester, - delegation_id: None, + authorization_id: None, ctype_hash: ctype::mock::get_ctype_hash::(true), revoked: false, deposit: Deposit::, BalanceOf> { @@ -82,6 +74,82 @@ where } } +/// Authorize iff the subject of the origin and the provided attester id match. +#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, Eq)] +#[scale_info(skip_type_params(T))] +pub struct MockAccessControl(pub T::AttesterId); + +impl AttestationAccessControl, ClaimHashOf> + for MockAccessControl +where + T: Config::AttesterId>, +{ + fn can_attest( + &self, + who: &T::AttesterId, + _ctype: &CtypeHashOf, + _claim: &ClaimHashOf, + ) -> Result { + if who == &self.0 { + Ok(0) + } else { + Err(DispatchError::Other("Unauthorized")) + } + } + + fn can_revoke( + &self, + who: &T::AttesterId, + _ctype: &CtypeHashOf, + _claim: &ClaimHashOf, + authorization_id: &T::AuthorizationId, + ) -> Result { + if authorization_id == who { + Ok(0) + } else { + Err(DispatchError::Other("Unauthorized")) + } + } + + fn can_remove( + &self, + who: &T::AttesterId, + _ctype: &CtypeHashOf, + _claim: &ClaimHashOf, + authorization_id: &T::AuthorizationId, + ) -> Result { + if authorization_id == who { + Ok(0) + } else { + Err(DispatchError::Other("Unauthorized")) + } + } + + fn authorization_id(&self) -> T::AuthorizationId { + self.0.clone() + } + + fn can_attest_weight(&self) -> Weight { + 0 + } + fn can_revoke_weight(&self) -> Weight { + 0 + } + fn can_remove_weight(&self) -> Weight { + 0 + } +} + +pub fn insert_attestation(claim_hash: ClaimHashOf, details: AttestationDetails) { + crate::Pallet::::reserve_deposit(details.deposit.owner.clone(), details.deposit.amount) + .expect("Should have balance"); + + crate::Attestations::::insert(&claim_hash, details.clone()); + if let Some(delegation_id) = details.authorization_id.as_ref() { + crate::ExternalAttestations::::insert(delegation_id, claim_hash, true) + } +} + /// Mocks that are only used internally #[cfg(test)] pub(crate) mod runtime { @@ -92,29 +160,26 @@ pub(crate) mod runtime { use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, - MultiSigner, + MultiSignature, MultiSigner, }; use std::sync::Arc; - use delegation::{mock::DelegationHierarchyInitialization, DelegationNode}; - use kilt_support::{ - mock::{mock_origin, SubjectId}, - signature::EqualVerify, - }; - use runtime_common::constants::{attestation::ATTESTATION_DEPOSIT, delegation::DELEGATION_DEPOSIT, MILLI_KILT}; + use kilt_support::mock::{mock_origin, SubjectId}; use super::*; - use crate::Pallet; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; - type TestCtypeOwner = SubjectId; - type TestCtypeHash = runtime_common::Hash; - type TestDelegationNodeId = runtime_common::Hash; - type TestDelegatorId = SubjectId; - type TestClaimHash = runtime_common::Hash; - type TestBalance = runtime_common::Balance; + pub type Hash = sp_core::H256; + pub type Balance = u128; + pub type Signature = MultiSignature; + pub type AccountPublic = ::Signer; + pub type AccountId = ::AccountId; + + pub const UNIT: Balance = 10u128.pow(15); + pub const MILLI_UNIT: Balance = 10u128.pow(12); + pub const ATTESTATION_DEPOSIT: Balance = 10 * MILLI_UNIT; frame_support::construct_runtime!( pub enum Test where @@ -125,7 +190,6 @@ pub(crate) mod runtime { System: frame_system::{Pallet, Call, Config, Storage, Event}, Attestation: crate::{Pallet, Call, Storage, Event}, Ctype: ctype::{Pallet, Call, Storage, Event}, - Delegation: delegation::{Pallet, Call, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Event}, MockOrigin: mock_origin::{Pallet, Origin}, } @@ -141,9 +205,9 @@ pub(crate) mod runtime { type Call = Call; type Index = u64; type BlockNumber = u64; - type Hash = runtime_common::Hash; + type Hash = Hash; type Hashing = BlakeTwo256; - type AccountId = <::Signer as IdentifyAccount>::AccountId; + type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; type Event = (); @@ -152,7 +216,7 @@ pub(crate) mod runtime { type Version = (); type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type BaseCallFilter = frame_support::traits::Everything; @@ -165,13 +229,13 @@ pub(crate) mod runtime { } parameter_types! { - pub const ExistentialDeposit: TestBalance = MILLI_KILT; + pub const ExistentialDeposit: Balance = MILLI_UNIT; pub const MaxLocks: u32 = 50; pub const MaxReserves: u32 = 50; } impl pallet_balances::Config for Test { - type Balance = TestBalance; + type Balance = Balance; type DustRemoval = (); type Event = (); type ExistentialDeposit = ExistentialDeposit; @@ -183,13 +247,13 @@ pub(crate) mod runtime { } parameter_types! { - pub const Fee: TestBalance = 500; + pub const Fee: Balance = 500; } impl ctype::Config for Test { - type CtypeCreatorId = TestCtypeOwner; - type EnsureOrigin = mock_origin::EnsureDoubleOrigin; - type OriginSuccess = mock_origin::DoubleOrigin; + type CtypeCreatorId = SubjectId; + type EnsureOrigin = mock_origin::EnsureDoubleOrigin; + type OriginSuccess = mock_origin::DoubleOrigin; type Event = (); type WeightInfo = (); @@ -198,65 +262,44 @@ pub(crate) mod runtime { type FeeCollector = (); } - parameter_types! { - pub const MaxSignatureByteLength: u16 = 64; - pub const MaxParentChecks: u32 = 5; - pub const MaxRevocations: u32 = 5; - pub const MaxRemovals: u32 = 5; - #[derive(Clone)] - pub const MaxChildren: u32 = 1000; - pub const DelegationDeposit: TestBalance = DELEGATION_DEPOSIT; - } - - impl delegation::Config for Test { - type Signature = (Self::DelegationEntityId, Vec); - type DelegationSignatureVerification = EqualVerify>; - type DelegationEntityId = TestDelegatorId; - type DelegationNodeId = TestDelegationNodeId; - type EnsureOrigin = mock_origin::EnsureDoubleOrigin; - type OriginSuccess = mock_origin::DoubleOrigin; - type Event = (); - type MaxSignatureByteLength = MaxSignatureByteLength; - type MaxParentChecks = MaxParentChecks; - type MaxRevocations = MaxRevocations; - type MaxRemovals = MaxRemovals; - type MaxChildren = MaxChildren; - type WeightInfo = (); - - type Currency = Balances; - type Deposit = DelegationDeposit; - } - impl mock_origin::Config for Test { type Origin = Origin; - type AccountId = runtime_common::AccountId; + type AccountId = AccountId; type SubjectId = SubjectId; } parameter_types! { pub const MaxDelegatedAttestations: u32 = 1000; - pub const Deposit: TestBalance = ATTESTATION_DEPOSIT; + pub const Deposit: Balance = ATTESTATION_DEPOSIT; } impl Config for Test { - type EnsureOrigin = mock_origin::EnsureDoubleOrigin>; - type OriginSuccess = mock_origin::DoubleOrigin>; + type EnsureOrigin = mock_origin::EnsureDoubleOrigin>; + type OriginSuccess = mock_origin::DoubleOrigin>; type Event = (); type WeightInfo = (); type Currency = Balances; type Deposit = Deposit; type MaxDelegatedAttestations = MaxDelegatedAttestations; + type AttesterId = SubjectId; + type AuthorizationId = SubjectId; + type AccessControl = MockAccessControl; } - pub(crate) const ACCOUNT_00: runtime_common::AccountId = runtime_common::AccountId::new([1u8; 32]); - pub(crate) const ACCOUNT_01: runtime_common::AccountId = runtime_common::AccountId::new([2u8; 32]); + pub(crate) const ACCOUNT_00: AccountId = AccountId::new([1u8; 32]); + pub(crate) const ACCOUNT_01: AccountId = AccountId::new([2u8; 32]); pub(crate) const ALICE_SEED: [u8; 32] = [1u8; 32]; pub(crate) const BOB_SEED: [u8; 32] = [2u8; 32]; + pub(crate) const CHARLIE_SEED: [u8; 32] = [3u8; 32]; - const DEFAULT_CLAIM_HASH_SEED: u64 = 1u64; - const ALTERNATIVE_CLAIM_HASH_SEED: u64 = 2u64; + pub const CLAIM_HASH_SEED_01: u64 = 1u64; + pub const CLAIM_HASH_SEED_02: u64 = 2u64; + + pub fn claim_hash_from_seed(seed: u64) -> Hash { + Hash::from_low_u64_be(seed) + } pub fn ed25519_did_from_seed(seed: &[u8; 32]) -> SubjectId { MultiSigner::from(ed25519::Pair::from_seed(seed).public()) @@ -270,44 +313,18 @@ pub(crate) mod runtime { .into() } - pub fn get_claim_hash(default: bool) -> TestClaimHash { - if default { - TestClaimHash::from_low_u64_be(DEFAULT_CLAIM_HASH_SEED) - } else { - TestClaimHash::from_low_u64_be(ALTERNATIVE_CLAIM_HASH_SEED) - } - } - #[derive(Clone, Default)] pub struct ExtBuilder { - delegation_hierarchies: DelegationHierarchyInitialization, - delegations: Vec<(TestDelegationNodeId, DelegationNode)>, - /// initial ctypes & owners - ctypes: Vec<(TestCtypeHash, CtypeCreatorOf)>, + ctypes: Vec<(CtypeHashOf, CtypeCreatorOf)>, /// endowed accounts with balances balances: Vec<(AccountIdOf, BalanceOf)>, - attestations: Vec<(TestClaimHash, AttestationDetails)>, + attestations: Vec<(ClaimHashOf, AttestationDetails)>, } impl ExtBuilder { #[must_use] - pub fn with_delegation_hierarchies( - mut self, - delegation_hierarchies: DelegationHierarchyInitialization, - ) -> Self { - self.delegation_hierarchies = delegation_hierarchies; - self - } - - #[must_use] - pub fn with_delegations(mut self, delegations: Vec<(TestDelegationNodeId, DelegationNode)>) -> Self { - self.delegations = delegations; - self - } - - #[must_use] - pub fn with_ctypes(mut self, ctypes: Vec<(TestCtypeHash, CtypeCreatorOf)>) -> Self { + pub fn with_ctypes(mut self, ctypes: Vec<(CtypeHashOf, CtypeCreatorOf)>) -> Self { self.ctypes = ctypes; self } @@ -319,7 +336,7 @@ pub(crate) mod runtime { } #[must_use] - pub fn with_attestations(mut self, attestations: Vec<(TestClaimHash, AttestationDetails)>) -> Self { + pub fn with_attestations(mut self, attestations: Vec<(ClaimHashOf, AttestationDetails)>) -> Self { self.attestations = attestations; self } @@ -339,20 +356,8 @@ pub(crate) mod runtime { ctype::Ctypes::::insert(ctype.0, ctype.1.clone()); } - delegation::mock::initialize_pallet(self.delegations, self.delegation_hierarchies); - for (claim_hash, details) in self.attestations { - Pallet::::reserve_deposit(details.deposit.owner.clone(), details.deposit.amount) - .expect("Should have balance"); - - crate::Attestations::::insert(&claim_hash, details.clone()); - if let Some(delegation_id) = details.delegation_id.as_ref() { - crate::DelegatedAttestations::::try_mutate(delegation_id, |attestations| { - let attestations = attestations.get_or_insert_with(Default::default); - attestations.try_push(claim_hash) - }) - .expect("Couldn't initialise delegated attestation"); - } + insert_attestation(claim_hash, details); } }); diff --git a/pallets/attestation/src/tests.rs b/pallets/attestation/src/tests.rs index e0179fb852..0e7794b9ea 100644 --- a/pallets/attestation/src/tests.rs +++ b/pallets/attestation/src/tests.rs @@ -16,425 +16,174 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org +use ctype::mock::get_ctype_hash; use frame_support::{assert_noop, assert_ok}; -use sp_runtime::traits::Zero; +use sp_runtime::{traits::Zero, DispatchError}; -use ctype::mock as ctype_mock; -use delegation::mock::{self as delegation_mock, DELEGATION_ID_SEED_1, DELEGATION_ID_SEED_2}; use kilt_support::mock::mock_origin::DoubleOrigin; use crate::{ self as attestation, mock::{runtime::Balances, *}, - AttesterOf, Config, DelegatedAttestations, + AttestationAccessControl, AttesterOf, Config, }; // ############################################################################# -// submit_attestation_creation_operation +// add #[test] -fn attest_no_delegation_successful() { +fn test_attest_without_authorization() { let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - - let operation = generate_base_attestation_creation_details(claim_hash, attestation); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let ctype_hash = get_ctype_hash::(true); + let authorization_info = None; ExtBuilder::default() - .with_ctypes(vec![(operation.ctype_hash, attester.clone())]) + .with_ctypes(vec![(ctype_hash, attester.clone())]) .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) .build() .execute_with(|| { assert_ok!(Attestation::add( DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id + claim_hash, + ctype_hash, + authorization_info.clone() )); let stored_attestation = Attestation::attestations(&claim_hash).expect("Attestation should be present on chain."); - assert_eq!(stored_attestation.ctype_hash, operation.ctype_hash); + assert_eq!(stored_attestation.ctype_hash, ctype_hash); assert_eq!(stored_attestation.attester, attester); - assert_eq!(stored_attestation.delegation_id, operation.delegation_id); + assert_eq!( + stored_attestation.authorization_id, + authorization_info.map(|ac| ac.authorization_id()) + ); assert!(!stored_attestation.revoked); }); } #[test] -fn attest_with_delegation_successful() { +fn test_attest_authorized() { let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details(); - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attester.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - delegation_node.details.permissions = delegation::Permissions::ATTEST; - let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_creation_details(claim_hash, attestation); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let ctype = get_ctype_hash::(true); + let authorization_info = Some(MockAccessControl(attester.clone())); ExtBuilder::default() - .with_ctypes(vec![(operation.ctype_hash, attester.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - attester.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) + .with_ctypes(vec![(ctype, attester.clone())]) .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) .build() .execute_with(|| { assert_ok!(Attestation::add( DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id + claim_hash, + ctype, + authorization_info.clone() )); let stored_attestation = Attestation::attestations(&claim_hash).expect("Attestation should be present on chain."); + assert!(Attestation::external_attestations(attester.clone(), claim_hash)); - assert_eq!(stored_attestation.ctype_hash, operation.ctype_hash); + assert_eq!(stored_attestation.ctype_hash, ctype); assert_eq!(stored_attestation.attester, attester); - assert_eq!(stored_attestation.delegation_id, operation.delegation_id); - assert!(!stored_attestation.revoked); - - let delegated_attestations = Attestation::delegated_attestations(&delegation_id) - .expect("Attested delegation should be present on chain."); - - assert_eq!(delegated_attestations, vec![claim_hash]); - }); -} - -#[test] -fn ctype_not_present_attest_error() { - let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - - let operation = generate_base_attestation_creation_details(claim_hash, attestation); - - ExtBuilder::default() - .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .build() - .execute_with(|| { - assert_noop!( - Attestation::add( - DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id - ), - ctype::Error::::CTypeNotFound - ); - }); -} - -#[test] -fn duplicate_attest_error() { - let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - - let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - let operation = generate_base_attestation_creation_details(claim_hash, attestation.clone()); - - ExtBuilder::default() - .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(operation.ctype_hash, attester.clone())]) - .with_attestations(vec![(claim_hash, attestation)]) - .build() - .execute_with(|| { - assert_noop!( - Attestation::add( - DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id - ), - attestation::Error::::AlreadyAttested - ); - }); -} - -#[test] -fn delegation_not_found_attest_error() { - let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_creation_details(claim_hash, attestation); - - ExtBuilder::default() - .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(operation.ctype_hash, attester.clone())]) - .build() - .execute_with(|| { - assert_noop!( - Attestation::add( - DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id - ), - delegation::Error::::DelegationNotFound - ); - }); -} - -#[test] -fn delegation_revoked_attest_error() { - let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - // Delegation node does not have permissions to attest. - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attester.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - delegation_node.details.permissions = delegation::Permissions::ATTEST; - delegation_node.details.revoked = true; - let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_creation_details(claim_hash, attestation); - - ExtBuilder::default() - .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(operation.ctype_hash, attester.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - attester.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) - .build() - .execute_with(|| { - assert_noop!( - Attestation::add( - DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id - ), - attestation::Error::::DelegationRevoked - ); - }); -} - -#[test] -fn not_delegation_owner_attest_error() { - let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let alternative_owner = sr25519_did_from_seed(&BOB_SEED); - - let claim_hash = get_claim_hash(true); - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut delegation_node = delegation_mock::generate_base_delegation_node::( - hierarchy_root_id, - alternative_owner, - Some(hierarchy_root_id), - ACCOUNT_00, - ); - delegation_node.details.permissions = delegation::Permissions::ATTEST; - let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_creation_details(claim_hash, attestation); - - ExtBuilder::default() - .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(operation.ctype_hash, attester.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - attester.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) - .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .build() - .execute_with(|| { - assert_noop!( - Attestation::add( - DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id - ), - attestation::Error::::NotDelegatedToAttester + assert_eq!( + stored_attestation.authorization_id, + authorization_info.map(|ac| ac.authorization_id()) ); + assert!(!stored_attestation.revoked); }); } #[test] -fn unauthorised_permissions_attest_error() { +fn test_attest_unauthorized() { let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - // Delegation node does not have permissions to attest. - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attester.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_creation_details(claim_hash, attestation); + let bob: AttesterOf = sr25519_did_from_seed(&BOB_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let ctype = get_ctype_hash::(true); + let authorization_info = Some(MockAccessControl(bob)); ExtBuilder::default() + .with_ctypes(vec![(ctype, attester.clone())]) .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(operation.ctype_hash, attester.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - attester.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) .build() .execute_with(|| { - assert_noop!( + assert_eq!( Attestation::add( DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id + claim_hash, + ctype, + authorization_info ), - attestation::Error::::DelegationUnauthorizedToAttest + Err(DispatchError::Other("Unauthorized")) ); }); } #[test] -fn root_not_present_attest_error() { +fn test_attest_ctype_not_found() { let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - let alternative_hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(false); - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attester.clone(), - Some(alternative_hierarchy_root_id), - ACCOUNT_00, - ); - delegation_node.details.permissions = delegation::Permissions::ATTEST; - let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_creation_details(claim_hash, attestation); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let ctype_hash = get_ctype_hash::(true); ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(operation.ctype_hash, attester.clone())]) - .with_delegation_hierarchies(vec![( - alternative_hierarchy_root_id, - hierarchy_details, - attester.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) .build() .execute_with(|| { assert_noop!( Attestation::add( DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id + claim_hash, + ctype_hash, + None ), - delegation::Error::::HierarchyNotFound + ctype::Error::::CTypeNotFound ); }); } #[test] -fn root_ctype_mismatch_attest_error() { +fn test_attest_already_exists() { let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - let alternative_ctype_hash = ctype_mock::get_ctype_hash::(false); - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let mut hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - hierarchy_details.ctype_hash = alternative_ctype_hash; - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attester.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - delegation_node.details.permissions = delegation::Permissions::ATTEST; - let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_creation_details(claim_hash, attestation); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(operation.ctype_hash, attester.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - attester.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) + .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) + .with_attestations(vec![(claim_hash, attestation.clone())]) .build() .execute_with(|| { assert_noop!( Attestation::add( DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.ctype_hash, - operation.delegation_id + claim_hash, + attestation.ctype_hash, + None ), - attestation::Error::::CTypeMismatch + attestation::Error::::AlreadyAttested ); }); } // ############################################################################# -// submit_attestation_revocation_operation +// revoke #[test] -fn revoke_and_remove_direct_successful() { +fn test_revoke_remove() { let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); let attestation = generate_base_attestation::(revoker.clone(), ACCOUNT_00); - let operation = generate_base_attestation_revocation_details::(claim_hash); - ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { assert_ok!(Attestation::revoke( DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks + claim_hash, + None )); let stored_attestation = Attestation::attestations(claim_hash).expect("Attestation should be present on chain."); @@ -444,8 +193,8 @@ fn revoke_and_remove_direct_successful() { assert_ok!(Attestation::remove( DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks + claim_hash, + None )); assert!(Attestation::attestations(claim_hash).is_none()); assert!(Balances::reserved_balance(ACCOUNT_00).is_zero()); @@ -453,249 +202,65 @@ fn revoke_and_remove_direct_successful() { } #[test] -fn revoke_with_delegation_successful() { - let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - revoker.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - delegation_node.details.permissions = delegation::Permissions::ATTEST; - // Attestation owned by a different user, but delegation owned by the user - // submitting the operation. - let mut attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let mut operation = generate_base_attestation_revocation_details::(claim_hash); - // Set to 0 as we only need to check the delegation node itself and no parent. - operation.max_parent_checks = 0u32; - - ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01, ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - revoker.clone(), - ACCOUNT_01, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) - .build() - .execute_with(|| { - assert_ok!(Attestation::revoke( - DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - )); - let stored_attestation = - Attestation::attestations(operation.claim_hash).expect("Attestation should be present on chain."); - - assert!(stored_attestation.revoked); - }); -} - -#[test] -fn revoke_with_parent_delegation_successful() { - let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - let parent_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut parent_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - revoker.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - parent_node.details.permissions = delegation::Permissions::ATTEST; - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_2); - let delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attestation_owner.clone(), - Some(parent_id), - ACCOUNT_00, - ); - let mut attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let mut operation = generate_base_attestation_revocation_details::(claim_hash); - // Set to 1 as the delegation referenced in the attestation is the child of the - // node we want to use - operation.max_parent_checks = 1u32; - - ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01, ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - revoker.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(parent_id, parent_node), (delegation_id, delegation_node)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) - .build() - .execute_with(|| { - assert_ok!(Attestation::revoke( - DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - )); - let stored_attestation = - Attestation::attestations(claim_hash).expect("Attestation should be present on chain."); - - assert!(stored_attestation.revoked); - }); -} - -#[test] -fn revoke_parent_delegation_no_attestation_permissions_successful() { - let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - let parent_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut parent_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - revoker.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - parent_node.details.permissions = delegation::Permissions::DELEGATE; - - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_2); - let delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attestation_owner.clone(), - Some(parent_id), - ACCOUNT_00, - ); - - let mut attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let mut operation = generate_base_attestation_revocation_details::(claim_hash); - // Set to 1 as the delegation referenced in the attestation is the child of the - // node we want to use - operation.max_parent_checks = 1u32; +fn test_authorized_revoke() { + let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); + let revoker: AttesterOf = sr25519_did_from_seed(&BOB_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let authorization_info = Some(MockAccessControl(revoker.clone())); + let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); + attestation.authorization_id = Some(revoker.clone()); ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01, ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - revoker.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(parent_id, parent_node), (delegation_id, delegation_node)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, attester)]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { assert_ok!(Attestation::revoke( DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks + claim_hash, + authorization_info )); let stored_attestation = Attestation::attestations(claim_hash).expect("Attestation should be present on chain."); + assert!(Attestation::external_attestations(revoker.clone(), claim_hash)); assert!(stored_attestation.revoked); + assert_eq!(Balances::reserved_balance(ACCOUNT_00), ::Deposit::get()); }); } #[test] -fn revoke_parent_delegation_with_direct_delegation_revoked_successful() { - let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - let parent_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut parent_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - revoker.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - parent_node.details.permissions = delegation::Permissions::ATTEST; - - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_2); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attestation_owner.clone(), - Some(parent_id), - ACCOUNT_00, - ); - - delegation_node.details.revoked = true; - let mut attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); +fn test_unauthorized_revoke() { + let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); + let revoker: AttesterOf = sr25519_did_from_seed(&BOB_SEED); + let evil: AttesterOf = sr25519_did_from_seed(&CHARLIE_SEED); - let mut operation = generate_base_attestation_revocation_details::(claim_hash); - // Set to 1 as the delegation referenced in the attestation is the child of the - // node we want to use - operation.max_parent_checks = 1u32; + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let authorization_info = Some(MockAccessControl(revoker.clone())); + let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); + attestation.authorization_id = Some(revoker); ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01, ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - revoker.clone(), - ACCOUNT_01, - )]) - .with_delegations(vec![(parent_id, parent_node), (delegation_id, delegation_node)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, attester)]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { - assert_ok!(Attestation::revoke( - DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - )); - let stored_attestation = - Attestation::attestations(claim_hash).expect("Attestation should be present on chain."); - - assert!(stored_attestation.revoked); + assert_noop!( + Attestation::revoke(DoubleOrigin(ACCOUNT_00, evil).into(), claim_hash, authorization_info), + DispatchError::Other("Unauthorized") + ); }); } #[test] -fn attestation_not_present_revoke_error() { +fn test_revoke_not_found() { let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let authorization_info = Some(MockAccessControl(revoker.clone())); let attestation = generate_base_attestation::(revoker.clone(), ACCOUNT_00); - let operation = generate_base_attestation_revocation_details::(claim_hash); - ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) @@ -704,8 +269,8 @@ fn attestation_not_present_revoke_error() { assert_noop!( Attestation::revoke( DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks + claim_hash, + authorization_info ), attestation::Error::::AttestationNotFound ); @@ -713,449 +278,206 @@ fn attestation_not_present_revoke_error() { } #[test] -fn already_revoked_revoke_error() { +fn test_already_revoked() { let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let authorization_info = Some(MockAccessControl(revoker.clone())); // Attestation already revoked let mut attestation = generate_base_attestation::(revoker.clone(), ACCOUNT_00); attestation.revoked = true; - let operation = generate_base_attestation_revocation_details::(claim_hash); - ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { assert_noop!( Attestation::revoke( DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks + claim_hash, + authorization_info ), attestation::Error::::AlreadyRevoked ); }); } -#[test] -fn unauthorised_attestation_revoke_error() { - let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - // Attestation owned by a different user - let attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - - let operation = generate_base_attestation_revocation_details::(claim_hash); - - ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01, ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_attestations(vec![(operation.claim_hash, attestation)]) - .build() - .execute_with(|| { - assert_noop!( - Attestation::revoke( - DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - ), - attestation::Error::::Unauthorized - ); - }); -} - -#[test] -fn max_parent_lookups_revoke_error() { - let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - let parent_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let parent_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - revoker.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_2); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attestation_owner.clone(), - Some(parent_id), - ACCOUNT_00, - ); - - delegation_node.details.permissions = delegation::Permissions::ATTEST; - let mut attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let mut operation = generate_base_attestation_revocation_details::(claim_hash); - operation.max_parent_checks = 0u32; - - ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01, ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - revoker.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(parent_id, parent_node), (delegation_id, delegation_node)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) - .build() - .execute_with(|| { - assert_noop!( - Attestation::revoke( - DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - ), - delegation::Error::::MaxSearchDepthReached - ); - }); -} +// ############################################################################# +// remove attestation #[test] -fn revoked_delegation_revoke_error() { - let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - revoker.clone(), - Some(hierarchy_root_id), - ACCOUNT_00, - ); - delegation_node.details.permissions = delegation::Permissions::ATTEST; - delegation_node.details.revoked = true; - let mut attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_revocation_details::(claim_hash); +fn test_remove() { + let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); + let authorization_info = None; ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01, ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - revoker.clone(), - ACCOUNT_00, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { - assert_noop!( - Attestation::revoke( - DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - ), - attestation::Error::::Unauthorized - ); + assert_ok!(Attestation::remove( + DoubleOrigin(ACCOUNT_00, attester.clone()).into(), + claim_hash, + authorization_info + )); + assert!(Attestation::attestations(claim_hash).is_none()); }); } -// ############################################################################# -// remove attestation - #[test] -fn subject_remove_direct_successful() { - let revoker: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - let attestation = generate_base_attestation::(revoker.clone(), ACCOUNT_00); - - let operation = generate_base_attestation_revocation_details::(claim_hash); +fn test_remove_authorized() { + let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); + let revoker: AttesterOf = sr25519_did_from_seed(&BOB_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let mut attestation = generate_base_attestation::(attester, ACCOUNT_00); + attestation.authorization_id = Some(revoker.clone()); + let authorization_info = Some(MockAccessControl(revoker.clone())); ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) .with_ctypes(vec![(attestation.ctype_hash, revoker.clone())]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { assert_ok!(Attestation::remove( DoubleOrigin(ACCOUNT_00, revoker.clone()).into(), - operation.claim_hash, - operation.max_parent_checks + claim_hash, + authorization_info )); - assert!(Attestation::attestations(claim_hash).is_none()) + assert!(Attestation::attestations(claim_hash).is_none()); + assert!(!Attestation::external_attestations(revoker.clone(), claim_hash)); }); } #[test] -fn reclaim_deposit() { - let deposit_owner: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attester: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - let attestation = generate_base_attestation::(attester, ACCOUNT_00); - - let operation = generate_base_attestation_revocation_details::(claim_hash); +fn test_remove_unauthorized() { + let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); + let evil: AttesterOf = sr25519_did_from_seed(&BOB_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); + let authorization_info = Some(MockAccessControl(evil.clone())); ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(attestation.ctype_hash, deposit_owner)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_ctypes(vec![(attestation.ctype_hash, attester)]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { assert_noop!( - Attestation::reclaim_deposit(Origin::signed(ACCOUNT_01), operation.claim_hash), - attestation::Error::::Unauthorized, + Attestation::remove( + DoubleOrigin(ACCOUNT_00, evil.clone()).into(), + claim_hash, + authorization_info + ), + attestation::Error::::Unauthorized ); - assert_ok!(Attestation::reclaim_deposit( - Origin::signed(ACCOUNT_00), - operation.claim_hash, - )); - assert!(Attestation::attestations(claim_hash).is_none()) }); } #[test] -fn remove_with_delegation_successful() { +fn test_remove_not_found() { let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details::(); - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attester.clone(), - Some(hierarchy_root_id), - ACCOUNT_01, - ); - delegation_node.details.permissions = delegation::Permissions::ATTEST; - // Attestation owned by a different user, but delegation owned by the user - // submitting the operation. - let mut attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let mut operation = generate_base_attestation_revocation_details::(claim_hash); - // Set to 0 as we only need to check the delegation node itself and no parent. - operation.max_parent_checks = 0u32; + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01, ::Deposit::get() * 100), - ]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - attester.clone(), - ACCOUNT_01, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) .build() .execute_with(|| { - assert_eq!(Balances::reserved_balance(ACCOUNT_00), ::Deposit::get()); - assert_ok!(Attestation::remove( - DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - )); - assert!(Attestation::attestations(operation.claim_hash).is_none()); assert!(Balances::reserved_balance(ACCOUNT_00).is_zero()); + assert_noop!( + Attestation::remove(DoubleOrigin(ACCOUNT_00, attester.clone()).into(), claim_hash, None), + attestation::Error::::AttestationNotFound + ); }); } +// ############################################################################# +// reclaim deposit + #[test] -fn attestation_not_present_remove_error() { +fn test_reclaim_deposit() { let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let claim_hash = get_claim_hash(true); - - let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); - - let operation = generate_base_attestation_revocation_details::(claim_hash); + let other_authorized: AttesterOf = sr25519_did_from_seed(&BOB_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let mut attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); + attestation.authorization_id = Some(other_authorized.clone()); ExtBuilder::default() .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) - .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) + .with_ctypes(vec![(attestation.ctype_hash, attester)]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { - assert!(Balances::reserved_balance(ACCOUNT_00).is_zero()); - - assert_noop!( - Attestation::remove( - DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - ), - attestation::Error::::AttestationNotFound - ); + assert_eq!(Balances::reserved_balance(ACCOUNT_00), ::Deposit::get()); + assert_ok!(Attestation::reclaim_deposit(Origin::signed(ACCOUNT_00), claim_hash)); + assert!(!Attestation::external_attestations( + other_authorized.clone(), + claim_hash + )); + assert!(Attestation::attestations(claim_hash).is_none()); assert!(Balances::reserved_balance(ACCOUNT_00).is_zero()); }); } #[test] -fn unauthorised_attestation_remove_error() { - let remover: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - // Attestation owned by a different user - let attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - - let operation = generate_base_attestation_revocation_details::(claim_hash); +fn test_reclaim_deposit_authorization() { + let attester: AttesterOf = sr25519_did_from_seed(&BOB_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01.clone(), ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, remover.clone())]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, attester)]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { assert_eq!(Balances::reserved_balance(ACCOUNT_00), ::Deposit::get()); - assert_noop!( - Attestation::remove( - DoubleOrigin(ACCOUNT_00, remover.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - ), - attestation::Error::::Unauthorized - ); - assert_eq!(Balances::reserved_balance(ACCOUNT_00), ::Deposit::get()); + assert_ok!(Attestation::reclaim_deposit(Origin::signed(ACCOUNT_00), claim_hash)); + assert!(Attestation::attestations(claim_hash).is_none()); + assert!(Balances::reserved_balance(ACCOUNT_00).is_zero()); }); } #[test] -fn revoked_delegation_remove_error() { - let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details(); - - let delegation_id = delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1); - let mut delegation_node = delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attester.clone(), - Some(hierarchy_root_id), - ACCOUNT_01, - ); - - delegation_node.details.permissions = delegation::Permissions::ATTEST; - delegation_node.details.revoked = true; - let mut attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_revocation_details::(claim_hash); +fn test_reclaim_unauthorized() { + let attester: AttesterOf = sr25519_did_from_seed(&BOB_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01.clone(), ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - attester.clone(), - ACCOUNT_01, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, attester)]) + .with_attestations(vec![(claim_hash, attestation)]) .build() .execute_with(|| { - assert_eq!(Balances::reserved_balance(ACCOUNT_00), ::Deposit::get()); assert_noop!( - Attestation::remove( - DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - ), - attestation::Error::::Unauthorized + Attestation::reclaim_deposit(Origin::signed(ACCOUNT_01), claim_hash), + attestation::Error::::Unauthorized, ); - assert_eq!(Balances::reserved_balance(ACCOUNT_00), ::Deposit::get()); }); } #[test] -fn remove_delegated_attestation() { - let attester: AttesterOf = sr25519_did_from_seed(&ALICE_SEED); - let attestation_owner: AttesterOf = sr25519_did_from_seed(&BOB_SEED); - let claim_hash = get_claim_hash(true); - - let hierarchy_root_id = delegation_mock::get_delegation_hierarchy_id::(true); - let hierarchy_details = delegation_mock::generate_base_delegation_hierarchy_details(); - let (delegation_id, mut delegation_node) = ( - delegation_mock::delegation_id_from_seed::(DELEGATION_ID_SEED_1), - delegation_mock::generate_base_delegation_node( - hierarchy_root_id, - attester.clone(), - Some(hierarchy_root_id), - ACCOUNT_01, - ), - ); - delegation_node.details.permissions = delegation::Permissions::ATTEST; - let mut attestation = generate_base_attestation::(attestation_owner, ACCOUNT_00); - attestation.delegation_id = Some(delegation_id); - - let operation = generate_base_attestation_revocation_details::(claim_hash); +fn test_reclaim_deposit_not_found() { + let attester: AttesterOf = sr25519_did_from_seed(&BOB_SEED); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let attestation = generate_base_attestation::(attester.clone(), ACCOUNT_00); ExtBuilder::default() - .with_balances(vec![ - (ACCOUNT_00, ::Deposit::get() * 100), - (ACCOUNT_01, ::Deposit::get() * 100), - ]) - .with_ctypes(vec![(attestation.ctype_hash, attester.clone())]) - .with_delegation_hierarchies(vec![( - hierarchy_root_id, - hierarchy_details, - attester.clone(), - ACCOUNT_01, - )]) - .with_delegations(vec![(delegation_id, delegation_node)]) - .with_attestations(vec![(operation.claim_hash, attestation)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, attester)]) .build() .execute_with(|| { - assert_eq!(Balances::reserved_balance(ACCOUNT_00), ::Deposit::get()); - assert!( - DelegatedAttestations::::get(delegation_id) - .unwrap_or_default() - .iter() - .any(|&ch| ch == operation.claim_hash), - "delegated attestation entry should be present before removal" - ); - - assert_ok!(Attestation::remove( - DoubleOrigin(ACCOUNT_00, attester.clone()).into(), - operation.claim_hash, - operation.max_parent_checks - )); - assert!(Balances::reserved_balance(ACCOUNT_00).is_zero()); - assert!( - !DelegatedAttestations::::get(delegation_id) - .unwrap_or_default() - .iter() - .any(|&ch| ch == operation.claim_hash), - "delegated attestation entry should be removed" + assert_noop!( + Attestation::reclaim_deposit(Origin::signed(ACCOUNT_01), claim_hash), + attestation::Error::::AttestationNotFound, ); }); } diff --git a/pallets/ctype/Cargo.toml b/pallets/ctype/Cargo.toml index f0d426ad28..792ec1de0e 100644 --- a/pallets/ctype/Cargo.toml +++ b/pallets/ctype/Cargo.toml @@ -15,7 +15,6 @@ substrate-wasm-builder-runner = {version = "3.0.0"} [dev-dependencies] kilt-support = {features = ["mock"], path = "../../support"} pallet-balances = {branch = "polkadot-v0.9.17", default-features = false, git = "https://github.com/paritytech/substrate"} -runtime-common = {default-features = false, path = "../../runtimes/common"} serde = {version = "1.0.132"} sp-core = {branch = "polkadot-v0.9.17", default-features = false, git = "https://github.com/paritytech/substrate"} sp-keystore = {branch = "polkadot-v0.9.17", default-features = false, git = "https://github.com/paritytech/substrate"} @@ -28,7 +27,6 @@ serde = {optional = true, version = "1.0.132"} # Internal dependencies kilt-support = {default-features = false, path = "../../support"} -runtime-common = {default-features = false, optional = true, path = "../../runtimes/common"} # Substrate dependencies frame-benchmarking = {branch = "polkadot-v0.9.17", default-features = false, git = "https://github.com/paritytech/substrate", optional = true} @@ -44,7 +42,6 @@ sp-std = {branch = "polkadot-v0.9.17", default-features = false, git = "https:// [features] default = ["std"] mock = [ - "runtime-common", "pallet-balances", "serde", "sp-core", @@ -62,7 +59,6 @@ std = [ "frame-benchmarking/std", "frame-support/std", "frame-system/std", - "runtime-common/std", "kilt-support/std", "log/std", "pallet-balances/std", diff --git a/pallets/ctype/src/mock.rs b/pallets/ctype/src/mock.rs index 84282972b9..03919dacb7 100644 --- a/pallets/ctype/src/mock.rs +++ b/pallets/ctype/src/mock.rs @@ -36,21 +36,28 @@ where #[cfg(test)] pub mod runtime { - use frame_support::parameter_types; + use frame_support::{parameter_types, weights::constants::RocksDbWeight}; use kilt_support::mock::{mock_origin, SubjectId}; - use runtime_common::{Balance, Header, RocksDbWeight}; use sp_runtime::{ + testing::Header, traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, - AccountId32, + AccountId32, MultiSignature, }; use crate::{BalanceOf, Ctypes}; use super::*; - pub type TestCtypeHash = runtime_common::Hash; pub type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; pub type Block = frame_system::mocking::MockBlock; + pub type Hash = sp_core::H256; + pub type Balance = u128; + pub type Signature = MultiSignature; + pub type AccountPublic = ::Signer; + pub type AccountId = ::AccountId; + + pub const UNIT: Balance = 10u128.pow(15); + pub const MILLI_UNIT: Balance = 10u128.pow(12); frame_support::construct_runtime!( pub enum Test where @@ -75,9 +82,9 @@ pub mod runtime { type Call = Call; type Index = u64; type BlockNumber = u64; - type Hash = runtime_common::Hash; + type Hash = Hash; type Hashing = BlakeTwo256; - type AccountId = <::Signer as IdentifyAccount>::AccountId; + type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; type Event = (); @@ -118,7 +125,7 @@ pub mod runtime { impl mock_origin::Config for Test { type Origin = Origin; - type AccountId = runtime_common::AccountId; + type AccountId = AccountId; type SubjectId = SubjectId; } @@ -128,8 +135,8 @@ pub mod runtime { impl Config for Test { type CtypeCreatorId = SubjectId; - type EnsureOrigin = mock_origin::EnsureDoubleOrigin; - type OriginSuccess = mock_origin::DoubleOrigin; + type EnsureOrigin = mock_origin::EnsureDoubleOrigin; + type OriginSuccess = mock_origin::DoubleOrigin; type Event = (); type WeightInfo = (); @@ -139,21 +146,21 @@ pub mod runtime { } pub(crate) const DID_00: SubjectId = SubjectId(AccountId32::new([1u8; 32])); - pub(crate) const ACCOUNT_00: runtime_common::AccountId = runtime_common::AccountId::new([1u8; 32]); + pub(crate) const ACCOUNT_00: AccountId = AccountId::new([1u8; 32]); #[derive(Clone, Default)] pub(crate) struct ExtBuilder { - ctypes_stored: Vec<(TestCtypeHash, SubjectId)>, - balances: Vec<(runtime_common::AccountId, BalanceOf)>, + ctypes_stored: Vec<(CtypeHashOf, SubjectId)>, + balances: Vec<(AccountId, BalanceOf)>, } impl ExtBuilder { - pub(crate) fn with_ctypes(mut self, ctypes: Vec<(TestCtypeHash, SubjectId)>) -> Self { + pub(crate) fn with_ctypes(mut self, ctypes: Vec<(CtypeHashOf, SubjectId)>) -> Self { self.ctypes_stored = ctypes; self } - pub(crate) fn with_balances(mut self, balances: Vec<(runtime_common::AccountId, BalanceOf)>) -> Self { + pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, BalanceOf)>) -> Self { self.balances = balances; self } diff --git a/pallets/delegation/Cargo.toml b/pallets/delegation/Cargo.toml index 538714d8d6..135978ece2 100644 --- a/pallets/delegation/Cargo.toml +++ b/pallets/delegation/Cargo.toml @@ -13,9 +13,9 @@ targets = ["x86_64-unknown-linux-gnu"] substrate-wasm-builder-runner = {version = "3.0.0"} [dev-dependencies] +attestation = {features = ["mock"], path = "../attestation"} ctype = {features = ["mock"], path = "../ctype"} kilt-support = {features = ["mock"], path = "../../support"} -runtime-common = {default-features = false, path = "../../runtimes/common"} # External dependencies env_logger = {version = "0.8.4"} @@ -28,9 +28,9 @@ sp-keystore = {branch = "polkadot-v0.9.17", default-features = false, git = "htt [dependencies] # Internal dependencies +attestation = {default-features = false, path = "../attestation"} ctype = {default-features = false, path = "../ctype"} kilt-support = {default-features = false, path = "../../support"} -runtime-common = {default-features = false, path = "../../runtimes/common"} #External dependencies bitflags = {default-features = false, version = "1.3.2"} @@ -73,7 +73,6 @@ std = [ "ctype/std", "frame-support/std", "frame-system/std", - "runtime-common/std", "kilt-support/std", "log/std", "pallet-balances/std", diff --git a/pallets/delegation/src/access_control.rs b/pallets/delegation/src/access_control.rs new file mode 100644 index 0000000000..3702cd4c14 --- /dev/null +++ b/pallets/delegation/src/access_control.rs @@ -0,0 +1,511 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2022 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 . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{dispatch::Weight, ensure}; +use scale_info::TypeInfo; +use sp_runtime::DispatchError; + +use attestation::ClaimHashOf; +use ctype::CtypeHashOf; + +use crate::{ + default_weights::WeightInfo, Config, DelegationHierarchies, DelegationNodeIdOf, DelegationNodes, DelegatorIdOf, + Error, Pallet, Permissions, +}; + +/// Controls the access to attestations. +/// +/// Can attest if +/// * delegation node of sender is not revoked +/// * delegation node of sender has ATTEST permission +/// * the CType of the delegation root matches the CType of the attestation +/// +/// Can revoke attestations if +/// * delegation node of sender is not revoked +/// * sender delegation node is equal to OR parent of the delegation node +/// stored in the attestation +/// +/// Can remove attestations if +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct DelegationAc { + pub(crate) subject_node_id: DelegationNodeIdOf, + pub(crate) max_checks: u32, +} + +impl + attestation::AttestationAccessControl, DelegationNodeIdOf, CtypeHashOf, ClaimHashOf> + for DelegationAc +{ + fn can_attest( + &self, + who: &DelegatorIdOf, + ctype: &CtypeHashOf, + _claim: &ClaimHashOf, + ) -> Result { + let delegation_node = + DelegationNodes::::get(self.authorization_id()).ok_or(Error::::DelegationNotFound)?; + let root = + DelegationHierarchies::::get(delegation_node.hierarchy_root_id).ok_or(Error::::DelegationNotFound)?; + ensure!( + // has permission + ((delegation_node.details.permissions & Permissions::ATTEST) == Permissions::ATTEST) + // not revoked + && !delegation_node.details.revoked + // is owner of delegation + && &delegation_node.details.owner == who + // delegation matches the ctype + && &root.ctype_hash == ctype, + Error::::AccessDenied + ); + + Ok(::WeightInfo::can_attest()) + } + + fn can_revoke( + &self, + who: &DelegatorIdOf, + _ctype: &CtypeHashOf, + _claim: &ClaimHashOf, + attester_node_id: &DelegationNodeIdOf, + ) -> Result { + // NOTE: The node IDs of the sender (provided by the user through `who`) and + // attester (provided by the attestation pallet through on-chain storage) can be + // different! + match Pallet::::is_delegating(who, attester_node_id, self.max_checks)? { + (true, checks) => Ok(::WeightInfo::can_revoke(checks)), + _ => Err(Error::::AccessDenied.into()), + } + } + + fn can_remove( + &self, + who: &DelegatorIdOf, + ctype: &CtypeHashOf, + claim: &ClaimHashOf, + auth_id: &DelegationNodeIdOf, + ) -> Result { + self.can_revoke(who, ctype, claim, auth_id) + } + + fn authorization_id(&self) -> DelegationNodeIdOf { + self.subject_node_id + } + + fn can_attest_weight(&self) -> Weight { + ::WeightInfo::can_attest() + } + + fn can_revoke_weight(&self) -> Weight { + ::WeightInfo::can_revoke(self.max_checks) + } + + fn can_remove_weight(&self) -> Weight { + ::WeightInfo::can_remove(self.max_checks) + } +} + +#[cfg(test)] +mod tests { + use frame_support::{assert_noop, assert_ok}; + + use attestation::{mock::generate_base_attestation, AttestationAccessControl}; + use ctype::mock::get_ctype_hash; + use kilt_support::{deposit::Deposit, mock::mock_origin::DoubleOrigin}; + + use super::*; + use crate::{mock::*, DelegationDetails, DelegationNode}; + + #[test] + fn test_can_attest() { + let root_owner: DelegatorIdOf = sr25519_did_from_seed(&ALICE_SEED); + let delegate = sr25519_did_from_seed(&BOB_SEED); + + let hierarchy_root_id = get_delegation_hierarchy_id::(true); + let hierarchy_details = generate_base_delegation_hierarchy_details(); + let ctype_hash = hierarchy_details.ctype_hash; + let parent_id = delegation_id_from_seed::(DELEGATION_ID_SEED_1); + let parent_node = DelegationNode { + details: DelegationDetails { + owner: delegate.clone(), + permissions: Permissions::DELEGATE | Permissions::ATTEST, + revoked: false, + }, + children: Default::default(), + hierarchy_root_id, + parent: Some(hierarchy_root_id), + deposit: Deposit { + owner: ACCOUNT_00, + amount: ::Deposit::get(), + }, + }; + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let ac_info = Some(DelegationAc { + subject_node_id: parent_id, + max_checks: 1, + }); + + ExtBuilder::default() + .with_ctypes(vec![(ctype_hash, root_owner.clone())]) + .with_delegation_hierarchies(vec![(hierarchy_root_id, hierarchy_details, root_owner, ACCOUNT_00)]) + .with_delegations(vec![(parent_id, parent_node)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .build() + .execute_with(|| { + assert_ok!(Attestation::add( + DoubleOrigin(ACCOUNT_00, delegate.clone()).into(), + claim_hash, + ctype_hash, + ac_info.clone() + )); + let stored_attestation = + Attestation::attestations(&claim_hash).expect("Attestation should be present on chain."); + + assert_eq!(stored_attestation.ctype_hash, ctype_hash); + assert_eq!(stored_attestation.attester, delegate); + assert_eq!( + stored_attestation.authorization_id, + ac_info.map(|ac| ac.authorization_id()) + ); + assert!(!stored_attestation.revoked); + }); + } + + #[test] + fn test_cannot_attest_missing_permission() { + let root_owner: DelegatorIdOf = sr25519_did_from_seed(&ALICE_SEED); + let delegate = sr25519_did_from_seed(&BOB_SEED); + + let hierarchy_root_id = get_delegation_hierarchy_id::(true); + let hierarchy_details = generate_base_delegation_hierarchy_details(); + let ctype_hash = hierarchy_details.ctype_hash; + let parent_id = delegation_id_from_seed::(DELEGATION_ID_SEED_1); + let parent_node = DelegationNode { + details: DelegationDetails { + owner: delegate.clone(), + permissions: Permissions::DELEGATE, + revoked: false, + }, + children: Default::default(), + hierarchy_root_id, + parent: Some(hierarchy_root_id), + deposit: Deposit { + owner: ACCOUNT_00, + amount: ::Deposit::get(), + }, + }; + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let ac_info = Some(DelegationAc { + subject_node_id: parent_id, + max_checks: 1, + }); + + ExtBuilder::default() + .with_ctypes(vec![(ctype_hash, root_owner.clone())]) + .with_delegation_hierarchies(vec![(hierarchy_root_id, hierarchy_details, root_owner, ACCOUNT_00)]) + .with_delegations(vec![(parent_id, parent_node)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .build() + .execute_with(|| { + assert_noop!( + Attestation::add( + DoubleOrigin(ACCOUNT_00, delegate.clone()).into(), + claim_hash, + ctype_hash, + ac_info.clone() + ), + Error::::AccessDenied + ); + }); + } + + #[test] + fn test_can_attest_revoked() { + let root_owner: DelegatorIdOf = sr25519_did_from_seed(&ALICE_SEED); + let delegate = sr25519_did_from_seed(&BOB_SEED); + + let hierarchy_root_id = get_delegation_hierarchy_id::(true); + let hierarchy_details = generate_base_delegation_hierarchy_details(); + let ctype_hash = hierarchy_details.ctype_hash; + let parent_id = delegation_id_from_seed::(DELEGATION_ID_SEED_1); + let parent_node = DelegationNode { + details: DelegationDetails { + owner: delegate.clone(), + permissions: Permissions::DELEGATE | Permissions::ATTEST, + revoked: true, + }, + children: Default::default(), + hierarchy_root_id, + parent: Some(hierarchy_root_id), + deposit: Deposit { + owner: ACCOUNT_00, + amount: ::Deposit::get(), + }, + }; + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let ac_info = Some(DelegationAc { + subject_node_id: parent_id, + max_checks: 1, + }); + + ExtBuilder::default() + .with_ctypes(vec![(ctype_hash, root_owner.clone())]) + .with_delegation_hierarchies(vec![(hierarchy_root_id, hierarchy_details, root_owner, ACCOUNT_00)]) + .with_delegations(vec![(parent_id, parent_node)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .build() + .execute_with(|| { + assert_noop!( + Attestation::add( + DoubleOrigin(ACCOUNT_00, delegate.clone()).into(), + claim_hash, + ctype_hash, + ac_info.clone() + ), + Error::::AccessDenied + ); + }); + } + + #[test] + fn test_cannot_attest_missing_node() { + let root_owner: DelegatorIdOf = sr25519_did_from_seed(&ALICE_SEED); + let delegate = sr25519_did_from_seed(&BOB_SEED); + + let hierarchy_root_id = get_delegation_hierarchy_id::(true); + let hierarchy_details = generate_base_delegation_hierarchy_details(); + let ctype_hash = hierarchy_details.ctype_hash; + let parent_id = delegation_id_from_seed::(DELEGATION_ID_SEED_1); + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let ac_info = Some(DelegationAc { + subject_node_id: parent_id, + max_checks: 1, + }); + + ExtBuilder::default() + .with_ctypes(vec![(ctype_hash, root_owner.clone())]) + .with_delegation_hierarchies(vec![(hierarchy_root_id, hierarchy_details, root_owner, ACCOUNT_00)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .build() + .execute_with(|| { + assert_noop!( + Attestation::add( + DoubleOrigin(ACCOUNT_00, delegate.clone()).into(), + claim_hash, + ctype_hash, + ac_info.clone() + ), + Error::::DelegationNotFound + ); + }); + } + + #[test] + fn test_cannot_attest_wrong_ctype() { + let root_owner: DelegatorIdOf = sr25519_did_from_seed(&ALICE_SEED); + let delegate = sr25519_did_from_seed(&BOB_SEED); + + let hierarchy_root_id = get_delegation_hierarchy_id::(true); + let hierarchy_details = generate_base_delegation_hierarchy_details(); + let ctype_hash = get_ctype_hash::(false); + let parent_id = delegation_id_from_seed::(DELEGATION_ID_SEED_1); + let parent_node = DelegationNode { + details: DelegationDetails { + owner: delegate.clone(), + permissions: Permissions::DELEGATE | Permissions::ATTEST, + revoked: false, + }, + children: Default::default(), + hierarchy_root_id, + parent: Some(hierarchy_root_id), + deposit: Deposit { + owner: ACCOUNT_00, + amount: ::Deposit::get(), + }, + }; + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let ac_info = Some(DelegationAc { + subject_node_id: parent_id, + max_checks: 1, + }); + + ExtBuilder::default() + .with_ctypes(vec![(ctype_hash, root_owner.clone())]) + .with_delegation_hierarchies(vec![(hierarchy_root_id, hierarchy_details, root_owner, ACCOUNT_00)]) + .with_delegations(vec![(parent_id, parent_node)]) + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .build() + .execute_with(|| { + assert_noop!( + Attestation::add( + DoubleOrigin(ACCOUNT_00, delegate.clone()).into(), + claim_hash, + ctype_hash, + ac_info.clone() + ), + Error::::AccessDenied + ); + }); + } + + #[test] + fn test_can_revoke_same_node() { + let root_owner: DelegatorIdOf = sr25519_did_from_seed(&ALICE_SEED); + let delegate = sr25519_did_from_seed(&BOB_SEED); + + let hierarchy_root_id = get_delegation_hierarchy_id::(true); + let hierarchy_details = generate_base_delegation_hierarchy_details(); + let parent_id = delegation_id_from_seed::(DELEGATION_ID_SEED_1); + let parent_node = DelegationNode { + details: DelegationDetails { + owner: delegate.clone(), + permissions: Permissions::DELEGATE | Permissions::ATTEST, + revoked: false, + }, + children: Default::default(), + hierarchy_root_id, + parent: Some(hierarchy_root_id), + deposit: Deposit { + owner: ACCOUNT_00, + amount: ::Deposit::get(), + }, + }; + let ac_info = Some(DelegationAc { + subject_node_id: parent_id, + max_checks: 1, + }); + + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let mut attestation = generate_base_attestation::(delegate.clone(), ACCOUNT_00); + attestation.authorization_id = Some(parent_id); + + ExtBuilder::default() + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, delegate.clone())]) + .with_delegation_hierarchies(vec![(hierarchy_root_id, hierarchy_details, root_owner, ACCOUNT_00)]) + .with_delegations(vec![(parent_id, parent_node)]) + .with_attestations(vec![(claim_hash, attestation)]) + .build() + .execute_with(|| { + assert_ok!(Attestation::revoke( + DoubleOrigin(ACCOUNT_00, delegate.clone()).into(), + claim_hash, + ac_info + )); + }); + } + + #[test] + fn test_can_revoke_parent() { + let root_owner: DelegatorIdOf = sr25519_did_from_seed(&ALICE_SEED); + let delegate = sr25519_did_from_seed(&BOB_SEED); + + let hierarchy_root_id = get_delegation_hierarchy_id::(true); + let hierarchy_details = generate_base_delegation_hierarchy_details(); + let parent_id = delegation_id_from_seed::(DELEGATION_ID_SEED_1); + let parent_node = DelegationNode { + details: DelegationDetails { + owner: delegate.clone(), + permissions: Permissions::DELEGATE | Permissions::ATTEST, + revoked: false, + }, + children: Default::default(), + hierarchy_root_id, + parent: Some(hierarchy_root_id), + deposit: Deposit { + owner: ACCOUNT_00, + amount: ::Deposit::get(), + }, + }; + let ac_info = Some(DelegationAc { + subject_node_id: parent_id, + max_checks: 1, + }); + + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let mut attestation = generate_base_attestation::(delegate.clone(), ACCOUNT_00); + attestation.authorization_id = Some(parent_id); + + ExtBuilder::default() + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, delegate)]) + .with_delegation_hierarchies(vec![( + hierarchy_root_id, + hierarchy_details, + root_owner.clone(), + ACCOUNT_00, + )]) + .with_delegations(vec![(parent_id, parent_node)]) + .with_attestations(vec![(claim_hash, attestation)]) + .build() + .execute_with(|| { + assert_ok!(Attestation::revoke( + DoubleOrigin(ACCOUNT_00, root_owner.clone()).into(), + claim_hash, + ac_info + )); + }); + } + + #[test] + fn test_can_revoke_same_node_revoked() { + let root_owner: DelegatorIdOf = sr25519_did_from_seed(&ALICE_SEED); + let delegate = sr25519_did_from_seed(&BOB_SEED); + + let hierarchy_root_id = get_delegation_hierarchy_id::(true); + let hierarchy_details = generate_base_delegation_hierarchy_details(); + let parent_id = delegation_id_from_seed::(DELEGATION_ID_SEED_1); + let parent_node = DelegationNode { + details: DelegationDetails { + owner: delegate.clone(), + permissions: Permissions::DELEGATE | Permissions::ATTEST, + revoked: true, + }, + children: Default::default(), + hierarchy_root_id, + parent: Some(hierarchy_root_id), + deposit: Deposit { + owner: ACCOUNT_00, + amount: ::Deposit::get(), + }, + }; + let ac_info = Some(DelegationAc { + subject_node_id: parent_id, + max_checks: 1, + }); + + let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01); + let mut attestation = generate_base_attestation::(delegate.clone(), ACCOUNT_00); + attestation.authorization_id = Some(parent_id); + + ExtBuilder::default() + .with_balances(vec![(ACCOUNT_00, ::Deposit::get() * 100)]) + .with_ctypes(vec![(attestation.ctype_hash, delegate.clone())]) + .with_delegation_hierarchies(vec![(hierarchy_root_id, hierarchy_details, root_owner, ACCOUNT_00)]) + .with_delegations(vec![(parent_id, parent_node)]) + .with_attestations(vec![(claim_hash, attestation)]) + .build() + .execute_with(|| { + assert_ok!(Attestation::revoke( + DoubleOrigin(ACCOUNT_00, delegate.clone()).into(), + claim_hash, + ac_info + )); + }); + } +} diff --git a/pallets/delegation/src/benchmarking.rs b/pallets/delegation/src/benchmarking.rs index a915bab1b3..e3eecc6ab8 100644 --- a/pallets/delegation/src/benchmarking.rs +++ b/pallets/delegation/src/benchmarking.rs @@ -26,12 +26,14 @@ use frame_support::{ traits::{Currency, Get}, }; use frame_system::RawOrigin; -use kilt_support::{signature::VerifySignature, traits::GenerateBenchmarkOrigin}; use sp_core::{offchain::KeyTypeId, sr25519}; use sp_io::crypto::sr25519_generate; use sp_runtime::traits::Zero; use sp_std::{num::NonZeroU32, vec::Vec}; +use attestation::AttestationAccessControl; +use kilt_support::{signature::VerifySignature, traits::GenerateBenchmarkOrigin}; + const SEED: u32 = 0; const ONE_CHILD_PER_LEVEL: Option = NonZeroU32::new(1); @@ -376,6 +378,64 @@ benchmarks! { assert!(!DelegationNodes::::contains_key(leaf_id)); assert!(::Currency::reserved_balance(&sender).is_zero()); } + + can_attest { + let c = T::MaxParentChecks::get(); + + let ctype = Default::default(); + let claim = Default::default(); + + let sender: T::AccountId = account("sender", 0, SEED); + let (root_acc, _, leaf_acc, leaf_id) = setup_delegations::(c, ONE_CHILD_PER_LEVEL.expect(">0"), Permissions::DELEGATE | Permissions::ATTEST)?; + let root_acc: T::DelegationEntityId = root_acc.into(); + let leaf_acc: T::DelegationEntityId = leaf_acc.into(); + + let ac = DelegationAc::{ + subject_node_id: leaf_id, + max_checks: c + }; + + }: { ac.can_attest(&leaf_acc, &ctype, &claim).expect("Should be allowed") } + verify { + } + + can_revoke { + let c in 1 .. T::MaxParentChecks::get(); + + let ctype = Default::default(); + let claim = Default::default(); + + let sender: T::AccountId = account("sender", 0, SEED); + let (root_acc, root_id, _, leaf_id) = setup_delegations::(c, ONE_CHILD_PER_LEVEL.expect(">0"), Permissions::DELEGATE)?; + let root_acc: T::DelegationEntityId = root_acc.into(); + + let ac = DelegationAc::{ + subject_node_id: leaf_id, + max_checks: c + }; + + }: { ac.can_revoke(&root_acc, &ctype, &claim, &leaf_id).expect("Should be allowed") } + verify { + } + + can_remove { + let c in 1 .. T::MaxParentChecks::get(); + + let ctype = Default::default(); + let claim = Default::default(); + + let sender: T::AccountId = account("sender", 0, SEED); + let (root_acc, root_id, _, leaf_id) = setup_delegations::(c, ONE_CHILD_PER_LEVEL.expect(">0"), Permissions::DELEGATE)?; + let root_acc: T::DelegationEntityId = root_acc.into(); + + let ac = DelegationAc::{ + subject_node_id: leaf_id, + max_checks: c + }; + + }: { ac.can_remove(&root_acc, &ctype, &claim, &leaf_id).expect("Should be allowed") } + verify { + } } impl_benchmark_test_suite! { diff --git a/pallets/delegation/src/default_weights.rs b/pallets/delegation/src/default_weights.rs index fe8de75aab..ea0ee5080e 100644 --- a/pallets/delegation/src/default_weights.rs +++ b/pallets/delegation/src/default_weights.rs @@ -19,8 +19,8 @@ //! Autogenerated weights for delegation //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-11-08, STEPS: {{cmd.steps}}\, REPEAT: {{cmd.repeat}}\, LOW RANGE: {{cmd.lowest_range_values}}\, HIGH RANGE: {{cmd.highest_range_values}}\ -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 +//! DATE: 2022-02-22, STEPS: {{cmd.steps}}\, REPEAT: {{cmd.repeat}}\, LOW RANGE: {{cmd.lowest_range_values}}\, HIGH RANGE: {{cmd.highest_range_values}}\ +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/kilt-parachain @@ -36,7 +36,6 @@ // --output=pallets/delegation/src/default_weights.rs // --template=.maintain/weight-template.hbs - #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] @@ -53,6 +52,9 @@ pub trait WeightInfo { fn revoke_delegation_leaf(r: u32, c: u32, ) -> Weight; fn remove_delegation(r: u32, ) -> Weight; fn reclaim_deposit(r: u32, ) -> Weight; + fn can_attest() -> Weight; + fn can_revoke(c: u32, ) -> Weight; + fn can_remove(c: u32, ) -> Weight; } /// Weights for delegation using the Substrate node and recommended hardware. @@ -63,115 +65,175 @@ impl WeightInfo for SubstrateWeight { // Storage: System Account (r:1 w:1) // Storage: Delegation DelegationNodes (r:0 w:1) fn create_hierarchy() -> Weight { - (47_416_000_u64) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + (37_014_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: Delegation DelegationNodes (r:2 w:2) // Storage: System Account (r:1 w:1) fn add_delegation() -> Weight { - (57_220_000_u64) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + (44_986_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: Delegation DelegationNodes (r:1 w:1) // Storage: Delegation DelegationHierarchies (r:1 w:0) fn revoke_delegation_root_child(r: u32, c: u32, ) -> Weight { - (22_152_000_u64) - // Standard Error: 55_000 - .saturating_add((18_681_000_u64).saturating_mul(r as Weight)) - // Standard Error: 55_000 - .saturating_add((95_000_u64).saturating_mul(c as Weight)) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(r as Weight))) + (19_082_000 as Weight) + // Standard Error: 51_000 + .saturating_add((13_360_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 51_000 + .saturating_add((169_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } // Storage: Delegation DelegationNodes (r:6 w:1) // Storage: Delegation DelegationHierarchies (r:1 w:0) fn revoke_delegation_leaf(r: u32, c: u32, ) -> Weight { - (39_250_000_u64) - // Standard Error: 34_000 - .saturating_add((147_000_u64).saturating_mul(r as Weight)) - // Standard Error: 34_000 - .saturating_add((5_748_000_u64).saturating_mul(c as Weight)) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c as Weight))) - .saturating_add(T::DbWeight::get().writes(1_u64)) + (32_789_000 as Weight) + // Standard Error: 41_000 + .saturating_add((13_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 41_000 + .saturating_add((5_095_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Delegation DelegationNodes (r:2 w:2) // Storage: System Account (r:1 w:1) // Storage: Delegation DelegationHierarchies (r:1 w:1) fn remove_delegation(r: u32, ) -> Weight { - (61_171_000_u64) - // Standard Error: 99_000 - .saturating_add((37_949_000_u64).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r as Weight))) + (51_863_000 as Weight) + // Standard Error: 59_000 + .saturating_add((23_235_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } // Storage: Delegation DelegationNodes (r:2 w:2) // Storage: System Account (r:1 w:1) // Storage: Delegation DelegationHierarchies (r:0 w:1) fn reclaim_deposit(r: u32, ) -> Weight { - (49_884_000_u64) - // Standard Error: 64_000 - .saturating_add((38_418_000_u64).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(T::DbWeight::get().writes((2_u64).saturating_mul(r as Weight))) + (44_669_000 as Weight) + // Standard Error: 56_000 + .saturating_add((23_133_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) + } + // Storage: Delegation DelegationNodes (r:1 w:0) + // Storage: Delegation DelegationHierarchies (r:1 w:0) + fn can_attest() -> Weight { + (12_484_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + } + // Storage: Delegation DelegationNodes (r:2 w:0) + fn can_revoke(c: u32, ) -> Weight { + (8_127_000 as Weight) + // Standard Error: 38_000 + .saturating_add((5_164_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) + } + // Storage: Delegation DelegationNodes (r:2 w:0) + fn can_remove(c: u32, ) -> Weight { + (7_991_000 as Weight) + // Standard Error: 35_000 + .saturating_add((5_193_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) } } // For backwards compatibility and tests impl WeightInfo for () { + // Storage: Delegation DelegationHierarchies (r:1 w:1) + // Storage: Ctype Ctypes (r:1 w:0) + // Storage: System Account (r:1 w:1) + // Storage: Delegation DelegationNodes (r:0 w:1) fn create_hierarchy() -> Weight { - (47_416_000_u64) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + (37_014_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } + // Storage: Delegation DelegationNodes (r:2 w:2) + // Storage: System Account (r:1 w:1) fn add_delegation() -> Weight { - (57_220_000_u64) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + (44_986_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } + // Storage: Delegation DelegationNodes (r:1 w:1) + // Storage: Delegation DelegationHierarchies (r:1 w:0) fn revoke_delegation_root_child(r: u32, c: u32, ) -> Weight { - (22_152_000_u64) - // Standard Error: 55_000 - .saturating_add((18_681_000_u64).saturating_mul(r as Weight)) - // Standard Error: 55_000 - .saturating_add((95_000_u64).saturating_mul(c as Weight)) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(r as Weight))) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(r as Weight))) + (19_082_000 as Weight) + // Standard Error: 51_000 + .saturating_add((13_360_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 51_000 + .saturating_add((169_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } + // Storage: Delegation DelegationNodes (r:6 w:1) + // Storage: Delegation DelegationHierarchies (r:1 w:0) fn revoke_delegation_leaf(r: u32, c: u32, ) -> Weight { - (39_250_000_u64) - // Standard Error: 34_000 - .saturating_add((147_000_u64).saturating_mul(r as Weight)) - // Standard Error: 34_000 - .saturating_add((5_748_000_u64).saturating_mul(c as Weight)) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c as Weight))) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + (32_789_000 as Weight) + // Standard Error: 41_000 + .saturating_add((13_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 41_000 + .saturating_add((5_095_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } + // Storage: Delegation DelegationNodes (r:2 w:2) + // Storage: System Account (r:1 w:1) + // Storage: Delegation DelegationHierarchies (r:1 w:1) fn remove_delegation(r: u32, ) -> Weight { - (61_171_000_u64) - // Standard Error: 99_000 - .saturating_add((37_949_000_u64).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r as Weight))) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r as Weight))) + (51_863_000 as Weight) + // Standard Error: 59_000 + .saturating_add((23_235_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } + // Storage: Delegation DelegationNodes (r:2 w:2) + // Storage: System Account (r:1 w:1) + // Storage: Delegation DelegationHierarchies (r:0 w:1) fn reclaim_deposit(r: u32, ) -> Weight { - (49_884_000_u64) - // Standard Error: 64_000 - .saturating_add((38_418_000_u64).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(r as Weight))) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(RocksDbWeight::get().writes((2_u64).saturating_mul(r as Weight))) + (44_669_000 as Weight) + // Standard Error: 56_000 + .saturating_add((23_133_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) + } + // Storage: Delegation DelegationNodes (r:1 w:0) + // Storage: Delegation DelegationHierarchies (r:1 w:0) + fn can_attest() -> Weight { + (12_484_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + } + // Storage: Delegation DelegationNodes (r:2 w:0) + fn can_revoke(c: u32, ) -> Weight { + (8_127_000 as Weight) + // Standard Error: 38_000 + .saturating_add((5_164_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) + } + // Storage: Delegation DelegationNodes (r:2 w:0) + fn can_remove(c: u32, ) -> Weight { + (7_991_000 as Weight) + // Standard Error: 35_000 + .saturating_add((5_193_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) } } diff --git a/pallets/delegation/src/lib.rs b/pallets/delegation/src/lib.rs index 8bb9d9118b..506a9db733 100644 --- a/pallets/delegation/src/lib.rs +++ b/pallets/delegation/src/lib.rs @@ -73,6 +73,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::unused_unit)] +mod access_control; pub mod default_weights; pub mod delegation_hierarchy; pub mod migrations; @@ -86,8 +87,9 @@ pub mod benchmarking; #[cfg(test)] mod tests; -pub use crate::{default_weights::WeightInfo, delegation_hierarchy::*, pallet::*}; +pub use crate::{access_control::DelegationAc, default_weights::WeightInfo, delegation_hierarchy::*, pallet::*}; +use codec::Encode; use frame_support::{ dispatch::DispatchResult, ensure, @@ -272,6 +274,8 @@ pub mod pallet { UnauthorizedRemoval, /// The delegation creator is not allowed to create the delegation. UnauthorizedDelegation, + /// The operation wasn't allowed because of insufficient rights. + AccessDenied, /// Max number of revocations for delegation nodes has been reached for /// the operation. ExceededRevocationBounds, diff --git a/pallets/delegation/src/mock.rs b/pallets/delegation/src/mock.rs index 6be1a55f55..b096f56a8f 100644 --- a/pallets/delegation/src/mock.rs +++ b/pallets/delegation/src/mock.rs @@ -30,6 +30,9 @@ use crate::{ DelegatorIdOf, Permissions, }; +#[cfg(test)] +pub use self::runtime::*; + const DEFAULT_HIERARCHY_ID_SEED: u64 = 1u64; const ALTERNATIVE_HIERARCHY_ID_SEED: u64 = 2u64; @@ -165,8 +168,8 @@ where } #[cfg(test)] -pub mod runtime { - use crate::BalanceOf; +pub(crate) mod runtime { + use crate::{BalanceOf, DelegateSignatureTypeOf, DelegationAc, DelegationNodeIdOf}; use super::*; @@ -176,24 +179,29 @@ pub mod runtime { use sp_keystore::{testing::KeyStore, KeystoreExt}; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, IdentifyAccount, IdentityLookup}, - MultiSigner, + traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify}, + MultiSignature, MultiSigner, }; use sp_std::sync::Arc; + use attestation::{mock::insert_attestation, AttestationDetails, ClaimHashOf}; use kilt_support::{ mock::{mock_origin, SubjectId}, signature::EqualVerify, }; - use runtime_common::constants::delegation::DELEGATION_DEPOSIT; - pub type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - pub type Block = frame_system::mocking::MockBlock; + pub(crate) type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + pub(crate) type Block = frame_system::mocking::MockBlock; + + pub(crate) type Hash = sp_core::H256; + pub(crate) type Balance = u128; + pub(crate) type Signature = MultiSignature; + pub(crate) type AccountPublic = ::Signer; + pub(crate) type AccountId = ::AccountId; - type TestDelegationNodeId = runtime_common::Hash; - type TestDelegateSignature = (SubjectId, Vec); - type TestBalance = runtime_common::Balance; - type TestCtypeHash = runtime_common::Hash; + pub(crate) const MILLI_UNIT: Balance = 10u128.pow(12); + pub(crate) const DELEGATION_DEPOSIT: Balance = 10 * MILLI_UNIT; + pub(crate) const ATTESTATION_DEPOSIT: Balance = 10 * MILLI_UNIT; frame_support::construct_runtime!( pub enum Test where @@ -202,10 +210,12 @@ pub mod runtime { UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Ctype: ctype::{Pallet, Call, Storage, Event}, - Delegation: delegation::{Pallet, Call, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Event}, - MockOrigin: mock_origin::{Pallet, Origin}, + + Attestation: attestation, + Ctype: ctype, + Delegation: delegation, + MockOrigin: mock_origin, } ); @@ -219,9 +229,9 @@ pub mod runtime { type Call = Call; type Index = u64; type BlockNumber = u64; - type Hash = runtime_common::Hash; + type Hash = Hash; type Hashing = BlakeTwo256; - type AccountId = runtime_common::AccountId; + type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; type Event = (); @@ -230,7 +240,7 @@ pub mod runtime { type Version = (); type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type BaseCallFilter = frame_support::traits::Everything; @@ -243,13 +253,13 @@ pub mod runtime { } parameter_types! { - pub const ExistentialDeposit: TestBalance = 0; + pub const ExistentialDeposit: Balance = 0; pub const MaxLocks: u32 = 50; pub const MaxReserves: u32 = 50; } impl pallet_balances::Config for Test { - type Balance = TestBalance; + type Balance = Balance; type DustRemoval = (); type Event = (); type ExistentialDeposit = ExistentialDeposit; @@ -262,18 +272,18 @@ pub mod runtime { impl mock_origin::Config for Test { type Origin = Origin; - type AccountId = runtime_common::AccountId; + type AccountId = AccountId; type SubjectId = SubjectId; } parameter_types! { - pub const Fee: TestBalance = 500; + pub const Fee: Balance = 500; } impl ctype::Config for Test { type CtypeCreatorId = SubjectId; - type EnsureOrigin = mock_origin::EnsureDoubleOrigin; - type OriginSuccess = mock_origin::DoubleOrigin; + type EnsureOrigin = mock_origin::EnsureDoubleOrigin; + type OriginSuccess = mock_origin::DoubleOrigin; type Event = (); type WeightInfo = (); @@ -282,6 +292,25 @@ pub mod runtime { type FeeCollector = (); } + parameter_types! { + pub const MaxDelegatedAttestations: u32 = 1000; + pub const Deposit: Balance = ATTESTATION_DEPOSIT; + } + + impl attestation::Config for Test { + type EnsureOrigin = mock_origin::EnsureDoubleOrigin>; + type OriginSuccess = mock_origin::DoubleOrigin>; + type Event = (); + type WeightInfo = (); + + type Currency = Balances; + type Deposit = Deposit; + type MaxDelegatedAttestations = MaxDelegatedAttestations; + type AttesterId = SubjectId; + type AuthorizationId = DelegationNodeIdOf; + type AccessControl = DelegationAc; + } + parameter_types! { pub const MaxSignatureByteLength: u16 = 64; pub const MaxParentChecks: u32 = 5; @@ -289,16 +318,16 @@ pub mod runtime { pub const MaxRemovals: u32 = 5; #[derive(Clone)] pub const MaxChildren: u32 = 1000; - pub const DepositMock: TestBalance = DELEGATION_DEPOSIT; + pub const DepositMock: Balance = DELEGATION_DEPOSIT; } impl Config for Test { - type Signature = TestDelegateSignature; + type Signature = (SubjectId, Vec); type DelegationSignatureVerification = EqualVerify>; type DelegationEntityId = SubjectId; - type DelegationNodeId = TestDelegationNodeId; - type EnsureOrigin = mock_origin::EnsureDoubleOrigin; - type OriginSuccess = mock_origin::DoubleOrigin; + type DelegationNodeId = Hash; + type EnsureOrigin = mock_origin::EnsureDoubleOrigin; + type OriginSuccess = mock_origin::DoubleOrigin; type Event = (); type MaxSignatureByteLength = MaxSignatureByteLength; type MaxParentChecks = MaxParentChecks; @@ -310,42 +339,48 @@ pub mod runtime { type WeightInfo = (); } - pub(crate) const ACCOUNT_00: runtime_common::AccountId = runtime_common::AccountId::new([1u8; 32]); - pub(crate) const ACCOUNT_01: runtime_common::AccountId = runtime_common::AccountId::new([2u8; 32]); - pub(crate) const ACCOUNT_02: runtime_common::AccountId = runtime_common::AccountId::new([3u8; 32]); + pub(crate) const ACCOUNT_00: AccountId = AccountId::new([1u8; 32]); + pub(crate) const ACCOUNT_01: AccountId = AccountId::new([2u8; 32]); + pub(crate) const ACCOUNT_02: AccountId = AccountId::new([3u8; 32]); pub(crate) const ALICE_SEED: [u8; 32] = [0u8; 32]; pub(crate) const BOB_SEED: [u8; 32] = [1u8; 32]; pub(crate) const CHARLIE_SEED: [u8; 32] = [2u8; 32]; - pub fn ed25519_did_from_seed(seed: &[u8; 32]) -> SubjectId { + pub(crate) const CLAIM_HASH_SEED_01: u64 = 1u64; + + pub(crate) fn claim_hash_from_seed(seed: u64) -> Hash { + Hash::from_low_u64_be(seed) + } + + pub(crate) fn ed25519_did_from_seed(seed: &[u8; 32]) -> SubjectId { MultiSigner::from(ed25519::Pair::from_seed(seed).public()) .into_account() .into() } - pub fn sr25519_did_from_seed(seed: &[u8; 32]) -> SubjectId { + pub(crate) fn sr25519_did_from_seed(seed: &[u8; 32]) -> SubjectId { MultiSigner::from(sr25519::Pair::from_seed(seed).public()) .into_account() .into() } - pub(crate) fn hash_to_u8(hash: T) -> Vec { + pub(crate) fn hash_to_u8(hash: Hash) -> Vec { hash.encode() } - pub struct DelegationCreationOperation { - pub delegation_id: TestDelegationNodeId, - pub hierarchy_id: TestDelegationNodeId, - pub parent_id: TestDelegationNodeId, + pub(crate) struct DelegationCreationOperation { + pub delegation_id: DelegationNodeIdOf, + pub hierarchy_id: DelegationNodeIdOf, + pub parent_id: DelegationNodeIdOf, pub delegate: SubjectId, pub permissions: Permissions, - pub delegate_signature: TestDelegateSignature, + pub delegate_signature: DelegateSignatureTypeOf, } - pub fn generate_base_delegation_creation_operation( - delegation_id: TestDelegationNodeId, - delegate_signature: TestDelegateSignature, + pub(crate) fn generate_base_delegation_creation_operation( + delegation_id: DelegationNodeIdOf, + delegate_signature: DelegateSignatureTypeOf, delegation_node: DelegationNode, ) -> DelegationCreationOperation { DelegationCreationOperation { @@ -360,30 +395,30 @@ pub mod runtime { } } - pub struct DelegationHierarchyRevocationOperation { - pub id: TestDelegationNodeId, + pub(crate) struct DelegationHierarchyRevocationOperation { + pub id: DelegationNodeIdOf, pub max_children: u32, } - pub fn generate_base_delegation_hierarchy_revocation_operation( - id: TestDelegationNodeId, + pub(crate) fn generate_base_delegation_hierarchy_revocation_operation( + id: DelegationNodeIdOf, ) -> DelegationHierarchyRevocationOperation { DelegationHierarchyRevocationOperation { id, max_children: 0u32 } } - pub struct DelegationRevocationOperation { - pub delegation_id: TestDelegationNodeId, + pub(crate) struct DelegationRevocationOperation { + pub delegation_id: DelegationNodeIdOf, pub max_parent_checks: u32, pub max_revocations: u32, } - pub struct DelegationDepositClaimOperation { - pub delegation_id: TestDelegationNodeId, + pub(crate) struct DelegationDepositClaimOperation { + pub delegation_id: DelegationNodeIdOf, pub max_removals: u32, } - pub fn generate_base_delegation_revocation_operation( - delegation_id: TestDelegationNodeId, + pub(crate) fn generate_base_delegation_revocation_operation( + delegation_id: DelegationNodeIdOf, ) -> DelegationRevocationOperation { DelegationRevocationOperation { delegation_id, @@ -392,8 +427,8 @@ pub mod runtime { } } - pub fn generate_base_delegation_deposit_claim_operation( - delegation_id: TestDelegationNodeId, + pub(crate) fn generate_base_delegation_deposit_claim_operation( + delegation_id: DelegationNodeIdOf, ) -> DelegationDepositClaimOperation { DelegationDepositClaimOperation { delegation_id, @@ -402,13 +437,14 @@ pub mod runtime { } #[derive(Clone, Default)] - pub struct ExtBuilder { + pub(crate) struct ExtBuilder { /// endowed accounts with balances balances: Vec<(AccountIdOf, BalanceOf)>, /// initial ctypes & owners - ctypes: Vec<(TestCtypeHash, SubjectId)>, - delegation_hierarchies_stored: DelegationHierarchyInitialization, - delegations_stored: Vec<(TestDelegationNodeId, DelegationNode)>, + ctypes: Vec<(CtypeHashOf, SubjectId)>, + delegation_hierarchies: DelegationHierarchyInitialization, + delegations: Vec<(DelegationNodeIdOf, DelegationNode)>, + attestations: Vec<(ClaimHashOf, AttestationDetails)>, } impl ExtBuilder { @@ -417,7 +453,7 @@ pub mod runtime { mut self, delegation_hierarchies: DelegationHierarchyInitialization, ) -> Self { - self.delegation_hierarchies_stored = delegation_hierarchies; + self.delegation_hierarchies = delegation_hierarchies; self } @@ -428,14 +464,20 @@ pub mod runtime { } #[must_use] - pub fn with_ctypes(mut self, ctypes: Vec<(TestCtypeHash, SubjectId)>) -> Self { + pub fn with_ctypes(mut self, ctypes: Vec<(CtypeHashOf, SubjectId)>) -> Self { self.ctypes = ctypes; self } #[must_use] - pub fn with_delegations(mut self, delegations: Vec<(TestDelegationNodeId, DelegationNode)>) -> Self { - self.delegations_stored = delegations; + pub fn with_delegations(mut self, delegations: Vec<(DelegationNodeIdOf, DelegationNode)>) -> Self { + self.delegations = delegations; + self + } + + #[must_use] + pub fn with_attestations(mut self, attestations: Vec<(ClaimHashOf, AttestationDetails)>) -> Self { + self.attestations = attestations; self } @@ -454,7 +496,11 @@ pub mod runtime { ctype::Ctypes::::insert(ctype_hash, owner); } - initialize_pallet(self.delegations_stored, self.delegation_hierarchies_stored); + initialize_pallet(self.delegations, self.delegation_hierarchies); + + for (claim_hash, details) in self.attestations { + insert_attestation(claim_hash, details) + } }); ext diff --git a/pallets/did/src/mock.rs b/pallets/did/src/mock.rs index fc5ddd9905..37fb2dcf15 100644 --- a/pallets/did/src/mock.rs +++ b/pallets/did/src/mock.rs @@ -429,26 +429,31 @@ pub struct ExtBuilder { } impl ExtBuilder { + #[must_use] pub fn with_dids(mut self, dids: Vec<(TestDidIdentifier, DidDetails)>) -> Self { self.dids_stored = dids; self } + #[must_use] pub fn with_endpoints(mut self, endpoints: Vec<(TestDidIdentifier, Vec>)>) -> Self { self.service_endpoints = endpoints; self } + #[must_use] pub(crate) fn with_balances(mut self, balances: Vec<(AccountIdOf, Balance)>) -> Self { self.balances = balances; self } + #[must_use] pub fn with_ctypes(mut self, ctypes: Vec<(TestCtypeHash, TestCtypeOwner)>) -> Self { self.ctypes_stored = ctypes; self } + #[must_use] pub fn with_deleted_dids(mut self, dids: Vec) -> Self { self.deleted_dids = dids; self diff --git a/pallets/kilt-launch/src/mock.rs b/pallets/kilt-launch/src/mock.rs index b3015893bf..8782cabaef 100644 --- a/pallets/kilt-launch/src/mock.rs +++ b/pallets/kilt-launch/src/mock.rs @@ -298,11 +298,13 @@ pub fn assert_balance(who: AccountId, free: Balance, usable_for_fees: Balance, u } impl ExtBuilder { + #[must_use] pub fn vest(mut self, vesting: Vec<(AccountId, BlockNumber, Balance)>) -> Self { self.vesting = vesting; self } + #[must_use] pub fn pseudos_vest_all(self) -> Self { self.vest(vec![ (PSEUDO_1, 10, 10_000), @@ -311,6 +313,7 @@ impl ExtBuilder { ]) } + #[must_use] pub fn lock_balance(mut self, balance_locks: Vec<(AccountId, BlockNumber, Balance)>) -> Self { self.balance_locks = balance_locks; self @@ -320,6 +323,7 @@ impl ExtBuilder { self.lock_balance(vec![(PSEUDO_1, 100, 1111), (PSEUDO_2, 1337, 2222)]) } + #[must_use] pub fn pseudos_lock_all(self) -> Self { self.lock_balance(vec![ (PSEUDO_1, 100, 10_000), diff --git a/pallets/pallet-did-lookup/src/mock.rs b/pallets/pallet-did-lookup/src/mock.rs index 529f565eeb..568aec4af9 100644 --- a/pallets/pallet-did-lookup/src/mock.rs +++ b/pallets/pallet-did-lookup/src/mock.rs @@ -128,11 +128,13 @@ pub struct ExtBuilder { } impl ExtBuilder { + #[must_use] pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { self.balances = balances; self } + #[must_use] pub fn with_connections(mut self, connections: Vec<(AccountId, SubjectId, AccountId)>) -> Self { self.connections = connections; self diff --git a/pallets/pallet-web3-names/src/mock.rs b/pallets/pallet-web3-names/src/mock.rs index d7bad03d90..1d1ddb4886 100644 --- a/pallets/pallet-web3-names/src/mock.rs +++ b/pallets/pallet-web3-names/src/mock.rs @@ -156,16 +156,19 @@ pub struct ExtBuilder { } impl ExtBuilder { + #[must_use] pub fn with_balances(mut self, balances: Vec<(TestWeb3NamePayer, Balance)>) -> Self { self.balances = balances; self } + #[must_use] pub fn with_web3_names(mut self, web3_names: Vec<(TestWeb3NameOwner, TestWeb3Name, TestWeb3NamePayer)>) -> Self { self.claimed_web3_names = web3_names; self } + #[must_use] pub fn with_banned_web3_names(mut self, web3_names: Vec) -> Self { self.banned_web3_names = web3_names; self diff --git a/pallets/parachain-staking/src/mock.rs b/pallets/parachain-staking/src/mock.rs index fcb671d90f..c3eb3960e8 100644 --- a/pallets/parachain-staking/src/mock.rs +++ b/pallets/parachain-staking/src/mock.rs @@ -246,21 +246,25 @@ impl Default for ExtBuilder { } impl ExtBuilder { + #[must_use] pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self { self.balances = balances; self } + #[must_use] pub(crate) fn with_collators(mut self, collators: Vec<(AccountId, Balance)>) -> Self { self.collators = collators; self } + #[must_use] pub(crate) fn with_delegators(mut self, delegators: Vec<(AccountId, AccountId, Balance)>) -> Self { self.delegators = delegators; self } + #[must_use] pub(crate) fn with_inflation( mut self, col_max: u64, @@ -280,6 +284,7 @@ impl ExtBuilder { self } + #[must_use] pub(crate) fn set_blocks_per_round(mut self, blocks_per_round: BlockNumber) -> Self { self.blocks_per_round = blocks_per_round; self diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index 00616cb905..9c3af569a4 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -13,6 +13,8 @@ scale-info = {version = "1.0", default-features = false, features = ["derive"]} serde = {version = "1.0.132", optional = true, features = ["derive"]} smallvec = "1.7.0" +attestation = {default-features = false, path = "../../pallets/attestation"} + frame-support = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.17"} frame-system = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.17"} pallet-authorship = {git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.17"} diff --git a/runtimes/common/src/authorization.rs b/runtimes/common/src/authorization.rs new file mode 100644 index 0000000000..50b45e1961 --- /dev/null +++ b/runtimes/common/src/authorization.rs @@ -0,0 +1,100 @@ +// KILT Blockchain – https://botlabs.org +// Copyright (C) 2019-2022 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 . + +// If you feel like getting in touch with us, you can do so at info@botlabs.org +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::dispatch::Weight; +use scale_info::TypeInfo; +use sp_runtime::DispatchError; + +use attestation::AttestationAccessControl; + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub enum AuthorizationId { + Delegation(DelegationId), +} + +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub enum PalletAuthorize { + Delegation(DelegationAc), +} + +impl + AttestationAccessControl, Ctype, ClaimHash> for PalletAuthorize +where + DelegationAc: AttestationAccessControl, +{ + fn can_attest( + &self, + who: &AttesterId, + ctype: &Ctype, + claim: &ClaimHash, + ) -> Result { + match self { + PalletAuthorize::Delegation(ac) => ac.can_attest(who, ctype, claim), + } + } + + fn can_revoke( + &self, + who: &AttesterId, + ctype: &Ctype, + claim: &ClaimHash, + auth_id: &AuthorizationId, + ) -> Result { + match (self, auth_id) { + (PalletAuthorize::Delegation(ac), AuthorizationId::Delegation(auth_id)) => { + ac.can_revoke(who, ctype, claim, auth_id) + } // _ => Err(DispatchError::Other("unauthorized")), + } + } + + fn can_remove( + &self, + who: &AttesterId, + ctype: &Ctype, + claim: &ClaimHash, + auth_id: &AuthorizationId, + ) -> Result { + match (self, auth_id) { + (PalletAuthorize::Delegation(ac), AuthorizationId::Delegation(auth_id)) => { + ac.can_remove(who, ctype, claim, auth_id) + } // _ => Err(DispatchError::Other("unauthorized")), + } + } + + fn authorization_id(&self) -> AuthorizationId { + match self { + PalletAuthorize::Delegation(ac) => AuthorizationId::Delegation(ac.authorization_id()), + } + } + + fn can_attest_weight(&self) -> Weight { + match self { + PalletAuthorize::Delegation(ac) => ac.can_attest_weight(), + } + } + fn can_revoke_weight(&self) -> Weight { + match self { + PalletAuthorize::Delegation(ac) => ac.can_revoke_weight(), + } + } + fn can_remove_weight(&self) -> Weight { + match self { + PalletAuthorize::Delegation(ac) => ac.can_remove_weight(), + } + } +} diff --git a/runtimes/common/src/constants.rs b/runtimes/common/src/constants.rs index 12b8aec40c..bbc38b6a42 100644 --- a/runtimes/common/src/constants.rs +++ b/runtimes/common/src/constants.rs @@ -100,7 +100,7 @@ pub mod attestation { use super::*; /// The size is checked in the runtime by a test. - pub const MAX_ATTESTATION_BYTE_LENGTH: u32 = 178; + pub const MAX_ATTESTATION_BYTE_LENGTH: u32 = 179; pub const ATTESTATION_DEPOSIT: Balance = deposit(2, MAX_ATTESTATION_BYTE_LENGTH); } diff --git a/runtimes/common/src/lib.rs b/runtimes/common/src/lib.rs index f6236a2f68..9ff9a63900 100644 --- a/runtimes/common/src/lib.rs +++ b/runtimes/common/src/lib.rs @@ -37,6 +37,7 @@ use sp_runtime::{ FixedPointNumber, MultiSignature, Perquintill, }; +pub mod authorization; pub mod constants; pub mod fees; pub mod pallet_id; diff --git a/runtimes/peregrine/src/lib.rs b/runtimes/peregrine/src/lib.rs index fd3e0e9890..cb4ea942c6 100644 --- a/runtimes/peregrine/src/lib.rs +++ b/runtimes/peregrine/src/lib.rs @@ -46,8 +46,10 @@ use sp_runtime::{ use sp_std::{cmp::Ordering, prelude::*}; use sp_version::RuntimeVersion; +use delegation::DelegationAc; pub use parachain_staking::InflationInfo; use runtime_common::{ + authorization::{AuthorizationId, PalletAuthorize}, constants::{self, KILT, MICRO_KILT, MILLI_KILT}, fees::{ToAuthor, WeightToFee}, pallet_id, AccountId, AuthorityId, Balance, BlockHashCount, BlockLength, BlockNumber, BlockWeights, DidIdentifier, @@ -510,6 +512,9 @@ impl attestation::Config for Runtime { type Currency = Balances; type Deposit = AttestationDeposit; type MaxDelegatedAttestations = MaxDelegatedAttestations; + type AttesterId = DidIdentifier; + type AuthorizationId = AuthorizationId<::DelegationNodeId>; + type AccessControl = PalletAuthorize>; } parameter_types! { diff --git a/runtimes/peregrine/src/weights/attestation.rs b/runtimes/peregrine/src/weights/attestation.rs index 4d9ba8b551..d038ec8c98 100644 --- a/runtimes/peregrine/src/weights/attestation.rs +++ b/runtimes/peregrine/src/weights/attestation.rs @@ -52,20 +52,16 @@ impl attestation::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - fn revoke(d: u32, ) -> Weight { + fn revoke() -> Weight { (37_043_000_u64) // Standard Error: 45_000 - .saturating_add((6_394_000_u64).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d as Weight))) .saturating_add(T::DbWeight::get().writes(1_u64)) } - fn remove(d: u32, ) -> Weight { + fn remove() -> Weight { (64_305_000_u64) // Standard Error: 41_000 - .saturating_add((6_297_000_u64).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d as Weight))) .saturating_add(T::DbWeight::get().writes(3_u64)) } fn reclaim_deposit() -> Weight { diff --git a/runtimes/peregrine/src/weights/delegation.rs b/runtimes/peregrine/src/weights/delegation.rs index 5b6656de9f..aca6480d81 100644 --- a/runtimes/peregrine/src/weights/delegation.rs +++ b/runtimes/peregrine/src/weights/delegation.rs @@ -19,8 +19,8 @@ //! Autogenerated weights for delegation //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-11-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 +//! DATE: 2022-02-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/kilt-parachain @@ -36,7 +36,6 @@ // --output=./runtimes/peregrine/src/weights/delegation.rs // --template=.maintain/runtime-weight-template.hbs - #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] @@ -53,25 +52,25 @@ impl delegation::WeightInfo for WeightInfo { // Storage: System Account (r:1 w:1) // Storage: Delegation DelegationNodes (r:0 w:1) fn create_hierarchy() -> Weight { - (48_478_000 as Weight) + (37_795_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: Delegation DelegationNodes (r:2 w:2) // Storage: System Account (r:1 w:1) fn add_delegation() -> Weight { - (59_829_000 as Weight) + (45_303_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: Delegation DelegationNodes (r:1 w:1) // Storage: Delegation DelegationHierarchies (r:1 w:0) fn revoke_delegation_root_child(r: u32, c: u32, ) -> Weight { - (23_761_000 as Weight) - // Standard Error: 52_000 - .saturating_add((19_062_000 as Weight).saturating_mul(r as Weight)) - // Standard Error: 52_000 - .saturating_add((43_000 as Weight).saturating_mul(c as Weight)) + (18_819_000 as Weight) + // Standard Error: 50_000 + .saturating_add((13_562_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 50_000 + .saturating_add((180_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) @@ -79,11 +78,11 @@ impl delegation::WeightInfo for WeightInfo { // Storage: Delegation DelegationNodes (r:6 w:1) // Storage: Delegation DelegationHierarchies (r:1 w:0) fn revoke_delegation_leaf(r: u32, c: u32, ) -> Weight { - (41_094_000 as Weight) - // Standard Error: 27_000 - .saturating_add((41_000 as Weight).saturating_mul(r as Weight)) - // Standard Error: 27_000 - .saturating_add((5_758_000 as Weight).saturating_mul(c as Weight)) + (32_036_000 as Weight) + // Standard Error: 35_000 + .saturating_add((133_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 35_000 + .saturating_add((5_171_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) @@ -92,24 +91,46 @@ impl delegation::WeightInfo for WeightInfo { // Storage: System Account (r:1 w:1) // Storage: Delegation DelegationHierarchies (r:1 w:1) fn remove_delegation(r: u32, ) -> Weight { - (63_362_000 as Weight) - // Standard Error: 83_000 - .saturating_add((38_970_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(r as Weight))) + (52_158_000 as Weight) + // Standard Error: 67_000 + .saturating_add((23_383_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } // Storage: Delegation DelegationNodes (r:2 w:2) // Storage: System Account (r:1 w:1) // Storage: Delegation DelegationHierarchies (r:0 w:1) fn reclaim_deposit(r: u32, ) -> Weight { - (53_098_000 as Weight) - // Standard Error: 75_000 - .saturating_add((39_041_000 as Weight).saturating_mul(r as Weight)) + (44_630_000 as Weight) + // Standard Error: 82_000 + .saturating_add((23_457_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) + } + // Storage: Delegation DelegationNodes (r:1 w:0) + // Storage: Delegation DelegationHierarchies (r:1 w:0) + fn can_attest() -> Weight { + (12_490_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + } + // Storage: Delegation DelegationNodes (r:2 w:0) + fn can_revoke(c: u32, ) -> Weight { + (8_147_000 as Weight) + // Standard Error: 37_000 + .saturating_add((5_115_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) + } + // Storage: Delegation DelegationNodes (r:2 w:0) + fn can_remove(c: u32, ) -> Weight { + (8_136_000 as Weight) + // Standard Error: 25_000 + .saturating_add((5_056_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) } } diff --git a/runtimes/spiritnet/src/lib.rs b/runtimes/spiritnet/src/lib.rs index 803a237c20..6e6808d675 100644 --- a/runtimes/spiritnet/src/lib.rs +++ b/runtimes/spiritnet/src/lib.rs @@ -46,8 +46,10 @@ use sp_runtime::{ use sp_std::{cmp::Ordering, prelude::*}; use sp_version::RuntimeVersion; +use delegation::DelegationAc; pub use parachain_staking::InflationInfo; use runtime_common::{ + authorization::{AuthorizationId, PalletAuthorize}, constants::{self, KILT, MICRO_KILT, MILLI_KILT}, fees::{ToAuthor, WeightToFee}, pallet_id, AccountId, AuthorityId, Balance, BlockHashCount, BlockLength, BlockNumber, BlockWeights, DidIdentifier, @@ -515,6 +517,9 @@ impl attestation::Config for Runtime { type Currency = Balances; type Deposit = AttestationDeposit; type MaxDelegatedAttestations = MaxDelegatedAttestations; + type AttesterId = DidIdentifier; + type AuthorizationId = AuthorizationId<::DelegationNodeId>; + type AccessControl = PalletAuthorize>; } parameter_types! { diff --git a/runtimes/spiritnet/src/weights/attestation.rs b/runtimes/spiritnet/src/weights/attestation.rs index 2b0ed7bcbe..3032ae2827 100644 --- a/runtimes/spiritnet/src/weights/attestation.rs +++ b/runtimes/spiritnet/src/weights/attestation.rs @@ -52,20 +52,16 @@ impl attestation::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - fn revoke(d: u32, ) -> Weight { + fn revoke() -> Weight { (37_282_000_u64) // Standard Error: 42_000 - .saturating_add((6_344_000_u64).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d as Weight))) .saturating_add(T::DbWeight::get().writes(1_u64)) } - fn remove(d: u32, ) -> Weight { + fn remove() -> Weight { (64_367_000_u64) // Standard Error: 58_000 - .saturating_add((6_230_000_u64).saturating_mul(d as Weight)) .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(d as Weight))) .saturating_add(T::DbWeight::get().writes(3_u64)) } fn reclaim_deposit() -> Weight { diff --git a/runtimes/spiritnet/src/weights/delegation.rs b/runtimes/spiritnet/src/weights/delegation.rs index 2d6a0802b8..ba9555e94b 100644 --- a/runtimes/spiritnet/src/weights/delegation.rs +++ b/runtimes/spiritnet/src/weights/delegation.rs @@ -19,8 +19,8 @@ //! Autogenerated weights for delegation //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-11-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("spiritnet-dev"), DB CACHE: 128 +//! DATE: 2022-02-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("spiritnet-dev"), DB CACHE: 1024 // Executed Command: // target/release/kilt-parachain @@ -36,7 +36,6 @@ // --output=./runtimes/spiritnet/src/weights/delegation.rs // --template=.maintain/runtime-weight-template.hbs - #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] @@ -53,25 +52,25 @@ impl delegation::WeightInfo for WeightInfo { // Storage: System Account (r:1 w:1) // Storage: Delegation DelegationNodes (r:0 w:1) fn create_hierarchy() -> Weight { - (48_311_000 as Weight) + (37_923_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: Delegation DelegationNodes (r:2 w:2) // Storage: System Account (r:1 w:1) fn add_delegation() -> Weight { - (59_067_000 as Weight) + (45_375_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: Delegation DelegationNodes (r:1 w:1) // Storage: Delegation DelegationHierarchies (r:1 w:0) fn revoke_delegation_root_child(r: u32, c: u32, ) -> Weight { - (21_963_000 as Weight) - // Standard Error: 46_000 - .saturating_add((19_693_000 as Weight).saturating_mul(r as Weight)) - // Standard Error: 46_000 - .saturating_add((139_000 as Weight).saturating_mul(c as Weight)) + (19_130_000 as Weight) + // Standard Error: 51_000 + .saturating_add((13_475_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 51_000 + .saturating_add((148_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) @@ -79,11 +78,11 @@ impl delegation::WeightInfo for WeightInfo { // Storage: Delegation DelegationNodes (r:6 w:1) // Storage: Delegation DelegationHierarchies (r:1 w:0) fn revoke_delegation_leaf(r: u32, c: u32, ) -> Weight { - (41_137_000 as Weight) - // Standard Error: 29_000 - .saturating_add((108_000 as Weight).saturating_mul(r as Weight)) - // Standard Error: 29_000 - .saturating_add((6_051_000 as Weight).saturating_mul(c as Weight)) + (32_418_000 as Weight) + // Standard Error: 39_000 + .saturating_add((71_000 as Weight).saturating_mul(r as Weight)) + // Standard Error: 39_000 + .saturating_add((5_175_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) @@ -92,24 +91,46 @@ impl delegation::WeightInfo for WeightInfo { // Storage: System Account (r:1 w:1) // Storage: Delegation DelegationHierarchies (r:1 w:1) fn remove_delegation(r: u32, ) -> Weight { - (62_336_000 as Weight) - // Standard Error: 87_000 - .saturating_add((39_220_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(r as Weight))) + (52_774_000 as Weight) + // Standard Error: 83_000 + .saturating_add((22_979_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } // Storage: Delegation DelegationNodes (r:2 w:2) // Storage: System Account (r:1 w:1) // Storage: Delegation DelegationHierarchies (r:0 w:1) fn reclaim_deposit(r: u32, ) -> Weight { - (53_370_000 as Weight) - // Standard Error: 126_000 - .saturating_add((39_346_000 as Weight).saturating_mul(r as Weight)) + (44_951_000 as Weight) + // Standard Error: 78_000 + .saturating_add((23_162_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) + } + // Storage: Delegation DelegationNodes (r:1 w:0) + // Storage: Delegation DelegationHierarchies (r:1 w:0) + fn can_attest() -> Weight { + (12_360_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + } + // Storage: Delegation DelegationNodes (r:2 w:0) + fn can_revoke(c: u32, ) -> Weight { + (8_163_000 as Weight) + // Standard Error: 45_000 + .saturating_add((5_153_000 as Weight).saturating_mul(c as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight))) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - .saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) + } + // Storage: Delegation DelegationNodes (r:2 w:0) + fn can_remove(c: u32, ) -> Weight { + (8_543_000 as Weight) + // Standard Error: 29_000 + .saturating_add((5_026_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(c as Weight))) } } diff --git a/runtimes/standalone/src/lib.rs b/runtimes/standalone/src/lib.rs index 270a327823..f7f94bf3d5 100644 --- a/runtimes/standalone/src/lib.rs +++ b/runtimes/standalone/src/lib.rs @@ -28,6 +28,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use codec::{Decode, Encode, MaxEncodedLen}; +use delegation::DelegationAc; use frame_support::traits::InstanceFilter; pub use frame_support::{ construct_runtime, parameter_types, @@ -63,6 +64,7 @@ pub use did; pub use pallet_balances::Call as BalancesCall; pub use pallet_web3_names; use runtime_common::{ + authorization::{AuthorizationId, PalletAuthorize}, constants::{self, KILT, MICRO_KILT, MILLI_KILT}, fees::ToAuthor, pallet_id, AccountId, Balance, BlockNumber, DidIdentifier, Hash, Index, Signature, SlowAdjustingFeeUpdate, @@ -316,6 +318,9 @@ impl attestation::Config for Runtime { type Currency = Balances; type Deposit = AttestationDeposit; type MaxDelegatedAttestations = MaxDelegatedAttestations; + type AttesterId = DidIdentifier; + type AuthorizationId = AuthorizationId<::DelegationNodeId>; + type AccessControl = PalletAuthorize>; } parameter_types! {