diff --git a/Cargo.lock b/Cargo.lock index e61d9cd77..b65013fe8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8904,6 +8904,7 @@ name = "runtime-common" version = "1.9.0-dev" dependencies = [ "attestation", + "ctype", "cumulus-primitives-core", "frame-support", "frame-system", diff --git a/pallets/attestation/src/benchmarking.rs b/pallets/attestation/src/benchmarking.rs index 456ee86cf..4447d8992 100644 --- a/pallets/attestation/src/benchmarking.rs +++ b/pallets/attestation/src/benchmarking.rs @@ -21,6 +21,7 @@ use frame_support::traits::{Currency, Get}; use frame_system::RawOrigin; use sp_runtime::traits::Hash; +use ctype::CtypeEntryOf; use kilt_support::traits::GenerateBenchmarkOrigin; use crate::*; @@ -33,6 +34,7 @@ benchmarks! { T: core::fmt::Debug, ::EnsureOrigin: GenerateBenchmarkOrigin, T: ctype::Config, + T::BlockNumber: From } add { @@ -41,7 +43,10 @@ benchmarks! { let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - ctype::Ctypes::::insert(ctype_hash, attester.clone()); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester.clone(), + created_at: 0u64.into() + }); ::Currency::make_free_balance_be(&sender, ::Deposit::get() + ::Deposit::get()); let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); @@ -66,7 +71,10 @@ benchmarks! { let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - ctype::Ctypes::::insert(ctype_hash, attester.clone()); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester.clone(), + created_at: 0u64.into() + }); ::Currency::make_free_balance_be(&sender, ::Deposit::get() + ::Deposit::get()); let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); @@ -92,7 +100,10 @@ benchmarks! { let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - ctype::Ctypes::::insert(ctype_hash, attester.clone()); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester.clone(), + created_at: 0u64.into() + }); ::Currency::make_free_balance_be(&sender, ::Deposit::get() + ::Deposit::get()); let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester.clone()); @@ -109,7 +120,10 @@ benchmarks! { let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - ctype::Ctypes::::insert(ctype_hash, attester.clone()); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester.clone(), + created_at: 0u64.into() + }); ::Currency::make_free_balance_be(&sender, ::Deposit::get() + ::Deposit::get()); let origin = ::EnsureOrigin::generate_origin(sender.clone(), attester); @@ -127,7 +141,10 @@ benchmarks! { let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - ctype::Ctypes::::insert(ctype_hash, attester.clone()); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester.clone(), + created_at: 0u64.into() + }); ::Currency::make_free_balance_be(&deposit_owner_old, ::Deposit::get() + ::Deposit::get()); ::Currency::make_free_balance_be(&deposit_owner_new, ::Deposit::get() + ::Deposit::get()); @@ -154,7 +171,10 @@ benchmarks! { let claim_hash: T::Hash = T::Hashing::hash(b"claim"); let ctype_hash: T::Hash = T::Hash::default(); - ctype::Ctypes::::insert(ctype_hash, attester.clone()); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester.clone(), + created_at: 0u64.into() + }); ::Currency::make_free_balance_be(&deposit_owner, ::Deposit::get() + ::Deposit::get()); let origin = ::EnsureOrigin::generate_origin(deposit_owner.clone(), attester.clone()); diff --git a/pallets/attestation/src/mock.rs b/pallets/attestation/src/mock.rs index 510f5f647..37eea8c76 100644 --- a/pallets/attestation/src/mock.rs +++ b/pallets/attestation/src/mock.rs @@ -168,8 +168,8 @@ pub fn insert_attestation(claim_hash: ClaimHashOf, details: Attest /// Mocks that are only used internally #[cfg(test)] pub(crate) mod runtime { - use ctype::CtypeCreatorOf; use frame_support::{parameter_types, weights::constants::RocksDbWeight}; + use frame_system::EnsureSigned; use sp_core::{ed25519, sr25519, Pair}; use sp_runtime::{ testing::Header, @@ -177,6 +177,7 @@ pub(crate) mod runtime { MultiSignature, MultiSigner, }; + use ctype::{CtypeCreatorOf, CtypeEntryOf}; use kilt_support::mock::{mock_origin, SubjectId}; use super::*; @@ -267,6 +268,7 @@ pub(crate) mod runtime { type CtypeCreatorId = SubjectId; type EnsureOrigin = mock_origin::EnsureDoubleOrigin; type OriginSuccess = mock_origin::DoubleOrigin; + type OverarchingOrigin = EnsureSigned; type RuntimeEvent = (); type WeightInfo = (); @@ -366,7 +368,13 @@ pub(crate) mod runtime { ext.execute_with(|| { for ctype in self.ctypes { - ctype::Ctypes::::insert(ctype.0, ctype.1.clone()); + ctype::Ctypes::::insert( + ctype.0, + CtypeEntryOf:: { + creator: ctype.1.clone(), + created_at: System::block_number(), + }, + ); } for (claim_hash, details) in self.attestations { diff --git a/pallets/ctype/src/benchmarking.rs b/pallets/ctype/src/benchmarking.rs index feea85cb1..899510e14 100644 --- a/pallets/ctype/src/benchmarking.rs +++ b/pallets/ctype/src/benchmarking.rs @@ -19,7 +19,7 @@ use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite}; use frame_support::{ sp_runtime::traits::Hash, - traits::{Currency, Get}, + traits::{Currency, EnsureOrigin, Get}, }; use sp_std::{ convert::{TryFrom, TryInto}, @@ -40,6 +40,7 @@ benchmarks! { <::Currency as Currency>>::Balance: TryFrom, <<::Currency as Currency<::AccountId>>::Balance as TryFrom>::Error: Debug, T::EnsureOrigin: GenerateBenchmarkOrigin, + T::BlockNumber: From, } add { @@ -57,10 +58,32 @@ benchmarks! { }: _(origin, ctype) verify { - let stored_ctype_creator: T::CtypeCreatorId = Ctypes::::get(ctype_hash).expect("CType hash should be present on chain."); + let stored_ctype_entry = Ctypes::::get(ctype_hash).expect("CType hash should be present on chain."); // Verify the CType has the right owner - assert_eq!(stored_ctype_creator, did); + assert_eq!(stored_ctype_entry.creator, did); + } + + set_block_number { + let caller = account("caller", 0, SEED); + let did: T::CtypeCreatorId = account("did", 0, SEED); + + let ctype: Vec = (0u8..u8::MAX).cycle().take(MAX_CTYPE_SIZE.try_into().unwrap()).collect(); + let ctype_hash = ::Hashing::hash(&ctype[..]); + let new_block_number = 500u64.into(); + + let initial_balance = ::Fee::get() * ctype.len().try_into().unwrap() + ::Currency::minimum_balance(); + ::Currency::make_free_balance_be(&caller, initial_balance); + let origin = T::EnsureOrigin::generate_origin(caller, did); + Pallet::::add(origin, ctype).expect("CType creation should not fail."); + let overarching_origin = T::OverarchingOrigin::successful_origin(); + + }: _(overarching_origin, ctype_hash, new_block_number) + verify { + let stored_ctype_entry = Ctypes::::get(ctype_hash).expect("CType hash should be present on chain."); + + // Verify the CType has the right block number + assert_eq!(stored_ctype_entry.created_at, new_block_number); } } diff --git a/pallets/ctype/src/ctype_entry.rs b/pallets/ctype/src/ctype_entry.rs new file mode 100644 index 000000000..6a0cbd2c7 --- /dev/null +++ b/pallets/ctype/src/ctype_entry.rs @@ -0,0 +1,12 @@ +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::RuntimeDebug; +use scale_info::TypeInfo; + +/// Creation details of a CType. +#[derive(Encode, Decode, RuntimeDebug, MaxEncodedLen, Eq, PartialEq, TypeInfo)] +pub struct CtypeEntry { + /// Identifier of the creator. + pub creator: Creator, + /// Block number in which the creation tx was dispatched. + pub created_at: BlockNumber, +} diff --git a/pallets/ctype/src/default_weights.rs b/pallets/ctype/src/default_weights.rs index fafe55643..9e2905c44 100644 --- a/pallets/ctype/src/default_weights.rs +++ b/pallets/ctype/src/default_weights.rs @@ -19,7 +19,7 @@ //! Autogenerated weights for ctype //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: {{cmd.steps}}\, REPEAT: {{cmd.repeat}}\, LOW RANGE: {{cmd.lowest_range_values}}\, HIGH RANGE: {{cmd.highest_range_values}}\ +//! DATE: 2022-11-30, 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: @@ -48,6 +48,7 @@ use sp_std::marker::PhantomData; /// Weight functions needed for ctype. pub trait WeightInfo { fn add(l: u32, ) -> Weight; + fn set_block_number() -> Weight; } /// Weights for ctype using the Substrate node and recommended hardware. @@ -63,6 +64,12 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } + // Storage: Ctype Ctypes (r:1 w:1) + fn set_block_number() -> Weight { + Weight::from_ref_time(20_685_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } } // For backwards compatibility and tests @@ -77,4 +84,10 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } + // Storage: Ctype Ctypes (r:1 w:1) + fn set_block_number() -> Weight { + Weight::from_ref_time(20_685_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } } diff --git a/pallets/ctype/src/lib.rs b/pallets/ctype/src/lib.rs index 2f24492e4..61758d8a2 100644 --- a/pallets/ctype/src/lib.rs +++ b/pallets/ctype/src/lib.rs @@ -42,6 +42,7 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub mod ctype_entry; pub mod default_weights; #[cfg(any(feature = "mock", test))] @@ -69,12 +70,16 @@ pub mod pallet { use sp_runtime::{traits::Saturating, SaturatedConversion}; use sp_std::vec::Vec; + use crate::ctype_entry::CtypeEntry; + /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); /// Type of a CType hash. pub type CtypeHashOf = ::Hash; + pub type CtypeEntryOf = CtypeEntry<::CtypeCreatorId, BlockNumberFor>; + /// Type of a CType creator. pub type CtypeCreatorOf = ::CtypeCreatorId; @@ -86,6 +91,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { type EnsureOrigin: EnsureOrigin<::RuntimeOrigin, Success = Self::OriginSuccess>; + type OverarchingOrigin: EnsureOrigin<::RuntimeOrigin>; type OriginSuccess: CallSources, CtypeCreatorOf>; type RuntimeEvent: From> + IsType<::RuntimeEvent>; type Currency: Currency>; @@ -105,10 +111,11 @@ pub mod pallet { /// CTypes stored on chain. /// - /// It maps from a CType hash to its creator. + /// It maps from a CType hash to its creator and block number in which it + /// was created. #[pallet::storage] #[pallet::getter(fn ctypes)] - pub type Ctypes = StorageMap<_, Blake2_128Concat, CtypeHashOf, CtypeCreatorOf>; + pub type Ctypes = StorageMap<_, Blake2_128Concat, CtypeHashOf, CtypeEntryOf>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -116,6 +123,9 @@ pub mod pallet { /// A new CType has been created. /// \[creator identifier, CType hash\] CTypeCreated(CtypeCreatorOf, CtypeHashOf), + /// Information about a CType has been updated. + /// \[CType hash\] + CTypeUpdated(CtypeHashOf), } #[pallet::error] @@ -177,11 +187,42 @@ pub mod pallet { T::FeeCollector::on_unbalanced(imbalance); log::debug!("Creating CType with hash {:?} and creator {:?}", hash, creator); - Ctypes::::insert(hash, creator.clone()); + Ctypes::::insert( + hash, + CtypeEntryOf:: { + creator: creator.clone(), + created_at: frame_system::Pallet::::block_number(), + }, + ); Self::deposit_event(Event::CTypeCreated(creator, hash)); Ok(()) } + + /// Set the creation block number for a given CType, if found. + /// + /// Emits `CTypeUpdated`. + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::set_block_number())] + pub fn set_block_number( + origin: OriginFor, + ctype_hash: CtypeHashOf, + block_number: BlockNumberFor, + ) -> DispatchResult { + T::OverarchingOrigin::ensure_origin(origin)?; + Ctypes::::try_mutate(ctype_hash, |ctype_entry| { + if let Some(ctype_entry) = ctype_entry { + ctype_entry.created_at = block_number; + Ok(()) + } else { + Err(Error::::CTypeNotFound) + } + })?; + + Self::deposit_event(Event::CTypeUpdated(ctype_hash)); + + Ok(()) + } } } diff --git a/pallets/ctype/src/mock.rs b/pallets/ctype/src/mock.rs index eb81a6bcc..e441c4c52 100644 --- a/pallets/ctype/src/mock.rs +++ b/pallets/ctype/src/mock.rs @@ -36,7 +36,8 @@ where #[cfg(test)] pub mod runtime { - use frame_support::{parameter_types, weights::constants::RocksDbWeight}; + use frame_support::{ord_parameter_types, parameter_types, weights::constants::RocksDbWeight}; + use frame_system::EnsureSignedBy; use kilt_support::mock::{mock_origin, SubjectId}; use sp_runtime::{ testing::Header, @@ -44,7 +45,7 @@ pub mod runtime { AccountId32, MultiSignature, }; - use crate::{BalanceOf, Ctypes}; + use crate::{BalanceOf, CtypeEntryOf, Ctypes}; use super::*; @@ -133,9 +134,14 @@ pub mod runtime { pub const Fee: Balance = 500; } + ord_parameter_types! { + pub const OverarchingOrigin: AccountId = ACCOUNT_00; + } + impl Config for Test { type CtypeCreatorId = SubjectId; type EnsureOrigin = mock_origin::EnsureDoubleOrigin; + type OverarchingOrigin = EnsureSignedBy; type OriginSuccess = mock_origin::DoubleOrigin; type RuntimeEvent = (); type WeightInfo = (); @@ -147,6 +153,7 @@ pub mod runtime { pub(crate) const DID_00: SubjectId = SubjectId(AccountId32::new([1u8; 32])); pub(crate) const ACCOUNT_00: AccountId = AccountId::new([1u8; 32]); + pub(crate) const ACCOUNT_01: AccountId = AccountId::new([2u8; 32]); #[derive(Clone, Default)] pub(crate) struct ExtBuilder { @@ -176,7 +183,13 @@ pub mod runtime { ext.execute_with(|| { for (ctype_hash, owner) in self.ctypes_stored.iter() { - Ctypes::::insert(ctype_hash, owner); + Ctypes::::insert( + ctype_hash, + CtypeEntryOf:: { + creator: owner.clone(), + created_at: System::block_number(), + }, + ); } }); diff --git a/pallets/ctype/src/tests.rs b/pallets/ctype/src/tests.rs index 4faff48cc..bd3572029 100644 --- a/pallets/ctype/src/tests.rs +++ b/pallets/ctype/src/tests.rs @@ -17,6 +17,9 @@ // If you feel like getting in touch with us, you can do so at info@botlabs.org use frame_support::{assert_noop, assert_ok, sp_runtime::traits::Hash}; +use frame_system::RawOrigin; +use sp_runtime::DispatchError; + use kilt_support::mock::mock_origin::DoubleOrigin; use crate::{self as ctype, mock::runtime::*}; @@ -34,14 +37,21 @@ fn check_successful_ctype_creation() { .with_balances(vec![(deposit_owner.clone(), initial_balance)]) .build() .execute_with(|| { + System::set_block_number(200); assert_ok!(Ctype::add( DoubleOrigin(deposit_owner.clone(), creator.clone()).into(), ctype )); let stored_ctype_creator = Ctype::ctypes(&ctype_hash).expect("CType hash should be present on chain."); - // Verify the CType has the right owner - assert_eq!(stored_ctype_creator, creator); + // Verify the CType has the right owner and block number + assert_eq!( + stored_ctype_creator, + ctype::CtypeEntryOf:: { + creator, + created_at: 200 + } + ); assert_eq!( Balances::free_balance(deposit_owner), initial_balance.saturating_sub(::Fee::get()) @@ -81,3 +91,60 @@ fn check_duplicate_ctype_creation() { ); }); } + +// set_block_number + +#[test] +fn set_block_number_ok() { + let creator = DID_00; + let ctype = [9u8; 256].to_vec(); + let ctype_hash = ::Hashing::hash(&ctype[..]); + let new_block_number = 500u64; + + ExtBuilder::default() + .with_ctypes(vec![(ctype_hash, creator)]) + .build() + .execute_with(|| { + assert_ok!(Ctype::set_block_number( + RawOrigin::Signed(ACCOUNT_00).into(), + ctype_hash, + new_block_number + )); + assert_eq!( + ctype::Ctypes::::get(ctype_hash) + .expect("CType with provided hash should exist.") + .created_at, + new_block_number + ); + }) +} + +#[test] +fn set_block_number_ctype_not_found() { + let ctype = [9u8; 256].to_vec(); + let ctype_hash = ::Hashing::hash(&ctype[..]); + + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + Ctype::set_block_number(RawOrigin::Signed(ACCOUNT_00).into(), ctype_hash, 100u64), + ctype::Error::::CTypeNotFound + ); + }) +} + +#[test] +fn set_block_number_bad_origin() { + let creator = DID_00; + let ctype = [9u8; 256].to_vec(); + let ctype_hash = ::Hashing::hash(&ctype[..]); + + ExtBuilder::default() + .with_ctypes(vec![(ctype_hash, creator)]) + .build() + .execute_with(|| { + assert_noop!( + Ctype::set_block_number(RawOrigin::Signed(ACCOUNT_01).into(), ctype_hash, 100u64), + DispatchError::BadOrigin + ); + }) +} diff --git a/pallets/delegation/src/benchmarking.rs b/pallets/delegation/src/benchmarking.rs index 7a144ab2d..01133d647 100644 --- a/pallets/delegation/src/benchmarking.rs +++ b/pallets/delegation/src/benchmarking.rs @@ -32,6 +32,7 @@ use sp_runtime::traits::Zero; use sp_std::{num::NonZeroU32, vec::Vec}; use attestation::AttestationAccessControl; +use ctype::CtypeEntryOf; use kilt_support::{signature::VerifySignature, traits::GenerateBenchmarkOrigin}; const SEED: u32 = 0; @@ -61,6 +62,7 @@ where T::DelegationNodeId: From, T::CtypeCreatorId: From, ::EnsureOrigin: GenerateBenchmarkOrigin, + T::BlockNumber: From, { log::info!("create delegation root"); let root_public = sr25519_generate(KeyTypeId(*b"aura"), None); @@ -74,7 +76,13 @@ where ::Currency::minimum_balance() + ::Deposit::get() + ::Deposit::get(), ); - ctype::Ctypes::::insert(ctype_hash, T::CtypeCreatorId::from(root_acc.clone())); + ctype::Ctypes::::insert( + ctype_hash, + CtypeEntryOf:: { + creator: T::CtypeCreatorId::from(root_acc.clone()), + created_at: 0u64.into(), + }, + ); Pallet::::create_hierarchy( ::EnsureOrigin::generate_origin(sender, root_acc.clone()), @@ -192,6 +200,7 @@ where <::DelegationSignatureVerification as VerifySignature>::Payload, )>, ::EnsureOrigin: GenerateBenchmarkOrigin, + T::BlockNumber: From, { let ( DelegationTriplet:: { @@ -227,6 +236,7 @@ benchmarks! { <::DelegationSignatureVerification as VerifySignature>::Payload, )>, ::EnsureOrigin: GenerateBenchmarkOrigin, + T::BlockNumber: From } create_hierarchy { @@ -234,7 +244,10 @@ benchmarks! { let creator: T::DelegationEntityId = account("creator", 0, SEED); let ctype = ::default(); let delegation = generate_delegation_id::(0); - ctype::Ctypes::::insert(ctype, ::CtypeCreatorId::from(creator.clone())); + ctype::Ctypes::::insert(ctype, CtypeEntryOf:: { + creator: T::CtypeCreatorId::from(creator.clone()), + created_at: 0u64.into(), + }); ::Currency::make_free_balance_be( &sender, ::Currency::minimum_balance() + ::Deposit::get(), diff --git a/pallets/delegation/src/mock.rs b/pallets/delegation/src/mock.rs index d98616665..01b95d690 100644 --- a/pallets/delegation/src/mock.rs +++ b/pallets/delegation/src/mock.rs @@ -175,6 +175,7 @@ pub(crate) mod runtime { use codec::Encode; use frame_support::{parameter_types, weights::constants::RocksDbWeight}; + use frame_system::EnsureSigned; use sp_core::{ed25519, sr25519, Pair}; use sp_runtime::{ testing::Header, @@ -183,6 +184,7 @@ pub(crate) mod runtime { }; use attestation::{mock::insert_attestation, AttestationDetails, ClaimHashOf}; + use ctype::CtypeEntryOf; use kilt_support::{ mock::{mock_origin, SubjectId}, signature::EqualVerify, @@ -282,6 +284,7 @@ pub(crate) mod runtime { type CtypeCreatorId = SubjectId; type EnsureOrigin = mock_origin::EnsureDoubleOrigin; type OriginSuccess = mock_origin::DoubleOrigin; + type OverarchingOrigin = EnsureSigned; type RuntimeEvent = (); type WeightInfo = (); @@ -491,7 +494,13 @@ pub(crate) mod runtime { ext.execute_with(|| { for (ctype_hash, owner) in self.ctypes.iter() { - ctype::Ctypes::::insert(ctype_hash, owner); + ctype::Ctypes::::insert( + ctype_hash, + CtypeEntryOf:: { + creator: owner.clone(), + created_at: System::block_number(), + }, + ); } initialize_pallet(self.delegations, self.delegation_hierarchies); diff --git a/pallets/did/src/mock.rs b/pallets/did/src/mock.rs index db38ed8a7..569398cb3 100644 --- a/pallets/did/src/mock.rs +++ b/pallets/did/src/mock.rs @@ -196,6 +196,7 @@ impl ctype::Config for Test { type OriginSuccess = DidRawOrigin; type CtypeCreatorId = DidIdentifier; + type OverarchingOrigin = EnsureSigned; type RuntimeEvent = (); type WeightInfo = (); type Currency = Balances; @@ -472,7 +473,13 @@ impl ExtBuilder { ext.execute_with(|| { for (ctype_hash, owner) in self.ctypes_stored.iter() { - ctype::Ctypes::::insert(ctype_hash, owner); + ctype::Ctypes::::insert( + ctype_hash, + ctype::CtypeEntryOf:: { + creator: owner.to_owned(), + created_at: System::block_number(), + }, + ); } for did in self.dids_stored.iter() { diff --git a/pallets/public-credentials/src/benchmarking.rs b/pallets/public-credentials/src/benchmarking.rs index fa9b96a7f..fbb928efd 100644 --- a/pallets/public-credentials/src/benchmarking.rs +++ b/pallets/public-credentials/src/benchmarking.rs @@ -24,6 +24,7 @@ use frame_support::{ }; use sp_std::{boxed::Box, vec, vec::Vec}; +use ctype::CtypeEntryOf; use kilt_support::{ deposit::Deposit, traits::{GenerateBenchmarkOrigin, GetWorstCase}, @@ -53,6 +54,7 @@ benchmarks! { ::EnsureOrigin: GenerateBenchmarkOrigin, ::SubjectId: GetWorstCase + Into> + sp_std::fmt::Debug, ::CredentialId: Default, + T::BlockNumber: From } add { @@ -70,7 +72,10 @@ benchmarks! { )); let credential_id = generate_credential_id::(&creation_op, &attester); - ctype::Ctypes::::insert(ctype_hash, attester.clone()); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester.clone(), + created_at: 0u64.into() + }); reserve_balance::(&sender); let origin = ::EnsureOrigin::generate_origin(sender, attester); }: _(origin, creation_op) @@ -97,7 +102,10 @@ benchmarks! { reserve_balance::(&sender); - ctype::Ctypes::::insert(ctype_hash, attester); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester, + created_at: 0u64.into() + }); Pallet::::add(origin.clone(), creation_op).expect("Pallet::add should not fail"); let credential_id_clone = credential_id.clone(); }: _(origin, credential_id_clone, None) @@ -123,7 +131,10 @@ benchmarks! { reserve_balance::(&sender); - ctype::Ctypes::::insert(ctype_hash, attester); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester, + created_at: 0u64.into() + }); Pallet::::add(origin.clone(), creation_op).expect("Pallet::add should not fail"); Pallet::::revoke(origin.clone(), credential_id.clone(), None).expect("Pallet::revoke should not fail"); let credential_id_clone = credential_id.clone(); @@ -149,7 +160,10 @@ benchmarks! { reserve_balance::(&sender); - ctype::Ctypes::::insert(ctype_hash, attester); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester, + created_at: 0u64.into() + }); Pallet::::add(origin.clone(), creation_op).expect("Pallet::add should not fail"); let credential_id_clone = credential_id.clone(); }: _(origin, credential_id_clone, None) @@ -175,7 +189,10 @@ benchmarks! { reserve_balance::(&sender); - ctype::Ctypes::::insert(ctype_hash, attester); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester, + created_at: 0u64.into() + }); Pallet::::add(origin, creation_op).expect("Pallet::add should not fail"); let origin = RawOrigin::Signed(sender); let credential_id_clone = credential_id.clone(); @@ -204,7 +221,10 @@ benchmarks! { reserve_balance::(&deposit_owner_old); reserve_balance::(&deposit_owner_new); - ctype::Ctypes::::insert(ctype_hash, attester.clone()); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester.clone(), + created_at: 0u64.into() + }); Pallet::::add(origin, creation_op).expect("Pallet::add should not fail"); let credential_id_clone = credential_id.clone(); let origin = ::EnsureOrigin::generate_origin(deposit_owner_new.clone(), attester); @@ -227,7 +247,10 @@ benchmarks! { let origin = ::EnsureOrigin::generate_origin(deposit_owner.clone(), attester.clone()); reserve_balance::(&deposit_owner); - ctype::Ctypes::::insert(ctype_hash, attester.clone()); + ctype::Ctypes::::insert(ctype_hash, CtypeEntryOf:: { + creator: attester.clone(), + created_at: 0u64.into() + }); let credential_entry = generate_base_credential_entry::( deposit_owner.clone(), diff --git a/pallets/public-credentials/src/mock.rs b/pallets/public-credentials/src/mock.rs index b8aeadbc2..b7d69f3d7 100644 --- a/pallets/public-credentials/src/mock.rs +++ b/pallets/public-credentials/src/mock.rs @@ -100,6 +100,7 @@ pub(crate) mod runtime { traits::{ConstU128, ConstU16, ConstU32, ConstU64}, weights::constants::RocksDbWeight, }; + use frame_system::EnsureSigned; use scale_info::TypeInfo; use sp_core::{sr25519, Pair}; use sp_runtime::{ @@ -110,7 +111,7 @@ pub(crate) mod runtime { use kilt_support::mock::{mock_origin, SubjectId}; - use ctype::{CtypeCreatorOf, CtypeHashOf}; + use ctype::{CtypeCreatorOf, CtypeEntryOf, CtypeHashOf}; use crate::{Config, CredentialEntryOf, Error, InputSubjectIdOf, PublicCredentialsAccessControl}; @@ -333,6 +334,7 @@ pub(crate) mod runtime { type CtypeCreatorId = SubjectId; type EnsureOrigin = mock_origin::EnsureDoubleOrigin; type OriginSuccess = mock_origin::DoubleOrigin; + type OverarchingOrigin = EnsureSigned; type RuntimeEvent = (); type WeightInfo = (); @@ -424,7 +426,13 @@ pub(crate) mod runtime { ext.execute_with(|| { for ctype in self.ctypes { - ctype::Ctypes::::insert(ctype.0, ctype.1.clone()); + ctype::Ctypes::::insert( + ctype.0, + CtypeEntryOf:: { + creator: ctype.1.clone(), + created_at: System::block_number(), + }, + ); } for (subject_id, credential_id, credential_entry) in self.public_credentials { diff --git a/runtimes/common/Cargo.toml b/runtimes/common/Cargo.toml index fb3251f77..91e85925b 100644 --- a/runtimes/common/Cargo.toml +++ b/runtimes/common/Cargo.toml @@ -23,6 +23,7 @@ smallvec.workspace = true # Internal dependencies attestation.workspace = true +ctype.workspace = true kilt-support.workspace = true parachain-staking.workspace = true public-credentials.workspace = true @@ -54,27 +55,29 @@ default = ["std"] fast-gov = [] runtime-benchmarks = [ "attestation/runtime-benchmarks", + "ctype/runtime-benchmarks", "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", + "frame-system/runtime-benchmarks", "kilt-support/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-membership/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-membership/runtime-benchmarks", "parachain-staking/runtime-benchmarks", "polkadot-parachain/runtime-benchmarks", - "public-credentials/runtime-benchmarks", + "public-credentials/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", ] std = [ "attestation/std", "codec/std", + "ctype/std", "cumulus-primitives-core/std", "frame-support/std", "frame-system/std", "kilt-asset-dids/std", "kilt-support/std", - "log/std", + "log/std", "pallet-authorship/std", "pallet-balances/std", "pallet-membership/std", @@ -93,14 +96,14 @@ std = [ "xcm/std", ] try-runtime = [ - "attestation/try-runtime", + "attestation/try-runtime", "frame-support/try-runtime", - "frame-system/try-runtime", - "kilt-support/try-runtime", - "pallet-authorship/try-runtime", - "pallet-balances/try-runtime", - "pallet-membership/try-runtime", - "pallet-transaction-payment/try-runtime", - "parachain-staking/try-runtime", - "public-credentials/try-runtime", + "frame-system/try-runtime", + "kilt-support/try-runtime", + "pallet-authorship/try-runtime", + "pallet-balances/try-runtime", + "pallet-membership/try-runtime", + "pallet-transaction-payment/try-runtime", + "parachain-staking/try-runtime", + "public-credentials/try-runtime", ] diff --git a/runtimes/common/src/migrations.rs b/runtimes/common/src/migrations.rs index 79e74681a..76f2a8e6b 100644 --- a/runtimes/common/src/migrations.rs +++ b/runtimes/common/src/migrations.rs @@ -15,3 +15,69 @@ // 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::traits::{GetStorageVersion, OnRuntimeUpgrade}; +use sp_runtime::traits::{Get, Zero}; +use sp_std::marker::PhantomData; + +use ctype::{CtypeCreatorOf, CtypeEntryOf}; + +#[cfg(feature = "try-runtime")] +use sp_std::vec::Vec; + +pub struct AddCTypeBlockNumber(PhantomData); + +impl OnRuntimeUpgrade for AddCTypeBlockNumber { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + assert_eq!(ctype::Pallet::::on_chain_storage_version(), 0,); + + // Use iter_keys() on new storage so it won't try to decode values. + let ctypes_to_migrate = ctype::Ctypes::::iter_keys().count() as u64; + + log::info!("🪪 CType pallet pre check: {:?} CTypes to migrate", ctypes_to_migrate); + Ok(ctypes_to_migrate.to_be_bytes().into()) + } + + fn on_runtime_upgrade() -> frame_support::weights::Weight { + let current = ctype::Pallet::::current_storage_version(); + let onchain = ctype::Pallet::::on_chain_storage_version(); + + log::info!( + "💰 Running migration with current storage version {:?} / onchain {:?}", + current, + onchain + ); + + let mut num_translations = 0u64; + let default_block_number = ::BlockNumber::zero(); + + ctype::Ctypes::::translate_values(|old: CtypeCreatorOf| { + num_translations = num_translations.saturating_add(1); + Some(CtypeEntryOf:: { + creator: old, + created_at: default_block_number, + }) + }); + current.put::>(); + + // Num translations + old version read and new version write + T::DbWeight::get().reads_writes(num_translations.saturating_add(1), num_translations.saturating_add(1)) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), &'static str> { + assert_eq!(ctype::Pallet::::on_chain_storage_version(), 1); + + let initial_ctype_count = u64::from_be_bytes(state.try_into().expect("input state should be 8 bytes")); + assert_eq!(initial_ctype_count, ctype::Ctypes::::iter().count() as u64); + // Verify all migrated ctypes can be decoded under the new type. + ctype::Ctypes::::iter_values().for_each(|v| assert!(v.created_at.is_zero())); + + log::info!( + "🪪 CType pallet post checks ok, all {:} CTypes have been migrated ✅", + initial_ctype_count + ); + Ok(()) + } +} diff --git a/runtimes/peregrine/src/lib.rs b/runtimes/peregrine/src/lib.rs index 57bfa54be..9c4309e8f 100644 --- a/runtimes/peregrine/src/lib.rs +++ b/runtimes/peregrine/src/lib.rs @@ -566,6 +566,7 @@ impl ctype::Config for Runtime { type EnsureOrigin = did::EnsureDidOrigin; type OriginSuccess = did::DidRawOrigin; + type OverarchingOrigin = EnsureRoot; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::ctype::WeightInfo; @@ -1066,6 +1067,7 @@ pub type Executive = frame_executive::Executive< pallet_preimage::migration::v1::Migration, pallet_scheduler::migration::v3::MigrateToV4, pallet_democracy::migrations::v1::Migration, + runtime_common::migrations::AddCTypeBlockNumber, EthereumMigration, ), >; diff --git a/runtimes/peregrine/src/weights/ctype.rs b/runtimes/peregrine/src/weights/ctype.rs index 955569286..cb9bfddb1 100644 --- a/runtimes/peregrine/src/weights/ctype.rs +++ b/runtimes/peregrine/src/weights/ctype.rs @@ -19,7 +19,7 @@ //! Autogenerated weights for ctype //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-11-30, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -57,4 +57,10 @@ impl ctype::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } + // Storage: Ctype Ctypes (r:1 w:1) + fn set_block_number() -> Weight { + Weight::from_ref_time(19_549_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } } diff --git a/runtimes/spiritnet/src/lib.rs b/runtimes/spiritnet/src/lib.rs index 2d48a4c01..871038318 100644 --- a/runtimes/spiritnet/src/lib.rs +++ b/runtimes/spiritnet/src/lib.rs @@ -561,6 +561,9 @@ impl ctype::Config for Runtime { type EnsureOrigin = did::EnsureDidOrigin; type OriginSuccess = did::DidRawOrigin; + // 3/5 of the technical committees can override the block number of one or more + // CTypes. + type OverarchingOrigin = EnsureRoot; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::ctype::WeightInfo; @@ -1060,6 +1063,7 @@ pub type Executive = frame_executive::Executive< pallet_preimage::migration::v1::Migration, pallet_scheduler::migration::v3::MigrateToV4, pallet_democracy::migrations::v1::Migration, + runtime_common::migrations::AddCTypeBlockNumber, EthereumMigration, ), >; diff --git a/runtimes/spiritnet/src/weights/ctype.rs b/runtimes/spiritnet/src/weights/ctype.rs index 9125837f4..5104c985d 100644 --- a/runtimes/spiritnet/src/weights/ctype.rs +++ b/runtimes/spiritnet/src/weights/ctype.rs @@ -57,4 +57,10 @@ impl ctype::WeightInfo for WeightInfo { .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } + // Storage: Ctype Ctypes (r:1 w:1) + fn set_block_number() -> Weight { + Weight::from_ref_time(19_549_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } } diff --git a/runtimes/standalone/src/lib.rs b/runtimes/standalone/src/lib.rs index 03f89f32b..64a3123a7 100644 --- a/runtimes/standalone/src/lib.rs +++ b/runtimes/standalone/src/lib.rs @@ -356,6 +356,7 @@ impl ctype::Config for Runtime { type CtypeCreatorId = DidIdentifier; type EnsureOrigin = did::EnsureDidOrigin; type OriginSuccess = did::DidRawOrigin; + type OverarchingOrigin = EnsureRoot; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); }