diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..d88507ad4c04a6e02f98f523cb80d604bc47e202 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,162 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'lc-core'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=lc-core" + ], + "filter": { + "name": "lc-core", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'lc-core'", + "cargo": { + "args": [ + "build" + ], + "filter": { + "name": "lc-core", + "kind": "bin" + } + }, + "args": [ + "--tmp", + "--dev", + "--execution", + "Native" + ], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'lc-core'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=lc-core", + "--package=lc-core" + ], + "filter": { + "name": "lc-core", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'lc-core-runtime'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=lc-core-runtime" + ], + "filter": { + "name": "lc-core-runtime", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'pallet-certification'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=pallet-certification" + ], + "filter": { + "name": "pallet-certification", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'pallet-identity'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=pallet-identity" + ], + "filter": { + "name": "pallet-identity", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'pallet-ud-accounts-storage'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=pallet-ud-accounts-storage" + ], + "filter": { + "name": "pallet-ud-accounts-storage", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'pallet-universal-dividend'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=pallet-universal-dividend" + ], + "filter": { + "name": "pallet-universal-dividend", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a4a7b4dc8711c03b0f1e7dc35b92bc1b08a9dd42..cf72eb9c26befcb121af3a4592d6a3fd73650a69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2742,6 +2742,7 @@ dependencies = [ "hex-literal", "pallet-aura", "pallet-balances", + "pallet-certification", "pallet-grandpa", "pallet-identity", "pallet-randomness-collective-flip", @@ -3910,6 +3911,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "maplit", "parity-scale-codec", "serde", "sp-core", diff --git a/Cargo.toml b/Cargo.toml index 615b817ae92db66717f200a3d79befeb33d2ae79..d286a46fe00811cf7e7c406d0de4d935c5b96fe1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,7 @@ members = [ 'pallets/universal-dividend', 'runtime', ] +[profile.dev] +opt-level = 3 [profile.release] panic = 'unwind' diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index 25f0d7c110f94089b33ece2d43534196fb3f4f89..a0a53294af87230069e92242985d60a94f921331 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -1,7 +1,7 @@ use lc_core_runtime::{ AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, IdentityConfig, IdtyDid, - IdtyRight, IdtyValue, Planet, Signature, SudoConfig, SystemConfig, UdAccountsStorageConfig, - UniversalDividendConfig, WASM_BINARY, + IdtyIndex, IdtyRight, IdtyValue, Planet, Signature, StrongCertConfig, SudoConfig, SystemConfig, + UdAccountsStorageConfig, UniversalDividendConfig, WASM_BINARY, }; use maplit::btreemap; use sc_service::ChainType; @@ -9,7 +9,7 @@ use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{sr25519, Pair, Public}; use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_runtime::traits::{IdentifyAccount, Verify}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>; @@ -183,11 +183,26 @@ fn testnet_genesis( identity: IdentityConfig { identities: initial_identities .iter() - .map(|(did, account)| { - IdtyValue::new_valid(*did, account.clone(), vec![IdtyRight::Ud]) + .map(|(did, account)| IdtyValue { + did: *did, + expire_on: lc_core_runtime::MaxInactivityPeriod::get(), + owner_key: account.clone(), + removable_on: 0, + renewable_on: lc_core_runtime::StrongCertRenewablePeriod::get(), + rights: vec![ + (IdtyRight::CreateIdty, None), + (IdtyRight::StrongCert, None), + (IdtyRight::Ud, None), + ], + status: lc_core_runtime::IdtyStatus::Validated, + data: Default::default(), }) .collect(), }, + strong_cert: StrongCertConfig { + certs_by_issuer: clique_wot(initial_identities.len()), + phantom: std::marker::PhantomData, + }, ud_accounts_storage: UdAccountsStorageConfig { ud_accounts: initial_identities.values().cloned().collect(), }, @@ -197,3 +212,16 @@ fn testnet_genesis( }, } } + +fn clique_wot(initial_identities_len: usize) -> BTreeMap<IdtyIndex, BTreeSet<IdtyIndex>> { + let mut certs_by_issuer = BTreeMap::new(); + for i in 1..=initial_identities_len { + certs_by_issuer.insert( + i as IdtyIndex, + (1..=initial_identities_len) + .filter_map(|j| if i != j { Some(j as IdtyIndex) } else { None }) + .collect(), + ); + } + certs_by_issuer +} diff --git a/pallets/certification/Cargo.toml b/pallets/certification/Cargo.toml index cc07b102da453d464a28a9e2bfed83d68e77e290..150f3ac7cfb34349c2224e7ad883936198049b85 100644 --- a/pallets/certification/Cargo.toml +++ b/pallets/certification/Cargo.toml @@ -75,6 +75,9 @@ version = '1.0.119' ### DEV ### +[dev-dependencies.maplit] +version = '1.0.2' + [dev-dependencies.sp-io] default-features = false git = 'https://github.com/paritytech/substrate.git' diff --git a/pallets/certification/src/lib.rs b/pallets/certification/src/lib.rs index 56e834d340f3f0299296a5b062e745a5378574cb..acda6feeec04af665fd3f2324273a5312220a14a 100644 --- a/pallets/certification/src/lib.rs +++ b/pallets/certification/src/lib.rs @@ -21,19 +21,23 @@ pub mod traits; #[cfg(test)] mod mock; +#[cfg(test)] +mod tests; + pub use pallet::*; use crate::traits::*; use codec::Codec; use sp_runtime::traits::{AtLeast32BitUnsigned, Zero}; -use sp_std::collections::btree_map::BTreeMap; -use sp_std::collections::btree_set::BTreeSet; -use sp_std::fmt::Debug; +use sp_std::{ + collections::{btree_map::BTreeMap, btree_set::BTreeSet}, + fmt::Debug, + vec::Vec, +}; pub mod pallet { use super::*; use frame_support::pallet_prelude::*; - //use frame_system::pallet_prelude::*; /// Configure the pallet by specifying the parameters and types on which it depends. pub trait Config<I: Instance = DefaultInstance>: frame_system::Config { @@ -166,7 +170,7 @@ pub mod pallet { let mut certs_by_receiver = BTreeMap::<T::IdtyIndex, Vec<T::IdtyIndex>>::new(); for (issuer, receivers) in &config.certs_by_issuer { assert!(!receivers.contains(issuer), "Identity cannot tcertify it-self."); - assert!(!receivers.len() <= T::MaxByIssuer::get() as usize, "Identity n°{:?} exceed MaxByIssuer.", issuer); + assert!(!receivers.len() >= T::MaxByIssuer::get() as usize, "Identity n°{:?} exceed MaxByIssuer.", issuer); cert_meta_by_issuer.insert(*issuer, IdtyCertMeta { issued_count: receivers.len() as u8, @@ -221,11 +225,13 @@ pub mod pallet { #[weight = 0] pub fn add_cert(origin, issuer: T::IdtyIndex, receiver: T::IdtyIndex) { T::AddCertOrigin::ensure_origin((origin, issuer, receiver))?; + frame_support::runtime_print!("add_cert({:?}, {:?}): origin OK", issuer, receiver); let block_number = frame_system::pallet::Pallet::<T>::block_number(); let (create, issuer_issued_count) = if let Ok(mut issuer_idty_cert_meta) = <StorageIdtyCertMeta<T, I>>::try_get(issuer) { // Verify rules CertPeriod and MaxByIssuer + frame_support::runtime_print!("add_cert({:?}, {:?}): Verify rules CertPeriod and MaxByIssuer", issuer, receiver); if issuer_idty_cert_meta.next_issuable_on > block_number { return Err(Error::<T, I>::NotRespectCertPeriod.into()); } else if issuer_idty_cert_meta.issued_count >= T::MaxByIssuer::get() { @@ -233,6 +239,7 @@ pub mod pallet { } // Verify rule RenewablePeriod + frame_support::runtime_print!("add_cert({:?}, {:?}): Verify rule RenewablePeriod", issuer, receiver); let create = if let Ok(CertValue { chainable_on, .. }) = <StorageCertsByIssuer<T, I>>::try_get(issuer, receiver) { if chainable_on > block_number { return Err(Error::<T, I>::NotRespectRenewablePeriod.into()); @@ -255,6 +262,7 @@ pub mod pallet { }; // Write StorageIdtyCertMeta for receiver + frame_support::runtime_print!("add_cert({:?}, {:?}): Write StorageIdtyCertMeta for receiver", issuer, receiver); let receiver_received_count = <StorageIdtyCertMeta<T, I>>::mutate_exists(receiver, |cert_meta_opt| { let cert_meta = cert_meta_opt.get_or_insert(IdtyCertMeta::default()); cert_meta.received_count = cert_meta.received_count.saturating_add(1); @@ -273,7 +281,7 @@ pub mod pallet { if create { // Write StorageCertsByReceiver <StorageCertsByReceiver<T, I>>::mutate_exists(receiver, |issuers_opt| { - let issuers = issuers_opt.get_or_insert(vec![]); + let issuers = issuers_opt.get_or_insert(Vec::with_capacity(0)); if let Err(index) = issuers.binary_search(&issuer) { issuers.insert(index, issuer); } diff --git a/pallets/certification/src/mock.rs b/pallets/certification/src/mock.rs index 3f9841c78480e523d610b25eb75c455d51bbd505..948ee9200c0208d0a1044fc19fc3d70e2cc6f852 100644 --- a/pallets/certification/src/mock.rs +++ b/pallets/certification/src/mock.rs @@ -30,7 +30,7 @@ use sp_runtime::{ type AccountId = u64; type BlockNumber = u64; type Block = frame_system::mocking::MockBlock<Test>; -type IdtyIndex = u64; +pub type IdtyIndex = u64; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>; // Configure a mock runtime to test the pallet. @@ -76,18 +76,13 @@ impl system::Config for Test { type OnSetCode = (); } -parameter_types! { - pub const MaxByIssuer: u8 = 3; - pub const RenewablePeriod: BlockNumber = 2; - pub const CertPeriod: u64 = 2; - pub const ValidityPeriod: u64 = 5; -} - pub struct EnsureRoot; impl frame_support::traits::EnsureOrigin<(Origin, IdtyIndex, IdtyIndex)> for EnsureRoot { type Success = (); - fn try_origin(o: (Origin, IdtyIndex, IdtyIndex)) -> Result<Self::Success, (Origin, IdtyIndex, IdtyIndex)> { + fn try_origin( + o: (Origin, IdtyIndex, IdtyIndex), + ) -> Result<Self::Success, (Origin, IdtyIndex, IdtyIndex)> { match o.0.clone().into() { Ok(system::RawOrigin::Root) => Ok(()), _ => Err(o), @@ -95,6 +90,13 @@ impl frame_support::traits::EnsureOrigin<(Origin, IdtyIndex, IdtyIndex)> for Ens } } +parameter_types! { + pub const MaxByIssuer: u8 = 3; + pub const RenewablePeriod: BlockNumber = 4; + pub const CertPeriod: u64 = 2; + pub const ValidityPeriod: u64 = 10; +} + impl pallet_certification::Config for Test { type AddCertOrigin = EnsureRoot; type CertPeriod = CertPeriod; diff --git a/pallets/certification/src/tests.rs b/pallets/certification/src/tests.rs index aadb17e7653daaa4ed408f771d6bccd3ac2747c0..3cc28928f68f86f483d75fcb5f452e3cb3771ce5 100644 --- a/pallets/certification/src/tests.rs +++ b/pallets/certification/src/tests.rs @@ -14,89 +14,70 @@ // You should have received a copy of the GNU Affero General Public License // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. -use crate::mock::IdtyIndex; use crate::mock::*; use crate::Error; -use frame_support::assert_err; use frame_support::assert_ok; use frame_system::{EventRecord, Phase}; +use maplit::{btreemap, btreeset}; +use sp_std::collections::btree_map::BTreeMap; #[test] -fn test_no_certs() { - let certifications = Vec::with_capacity(0); - new_test_ext(DefaultCertificationConfig { certifications }).execute_with(|| { - //assert_eq!(DefaultCertification::identities_count(), 0); +fn test_must_receive_cert_before_can_issue() { + new_test_ext(DefaultCertificationConfig { + certs_by_issuer: BTreeMap::new(), + phantom: core::marker::PhantomData, + }) + .execute_with(|| { + assert_eq!( + DefaultCertification::add_cert(Origin::root(), 0, 1), + Err(Error::<Test, _>::IdtyMustReceiveCertsBeforeCanIssue.into()) + ); }); } -/* -#[test] -fn test_two_identities() { - let identities = vec![ - crate::IdtyValue { - did: Did(0), - owner_key: 1, - removable_on: None, - rights: vec![(Right::Right2, Some(10))], - status: crate::IdtyStatus::Validated, - data: (), - }, - crate::IdtyValue { - did: Did(1), - owner_key: 2, - removable_on: None, - rights: vec![(Right::Right1, Some(20))], - status: crate::IdtyStatus::Validated, - data: (), - }, - ]; - - new_test_ext(IdentityConfig { identities }).execute_with(|| { - // Should have two identities - assert_eq!(Identity::identities_count(), 2); - // We need to initialize at least one block before any call - run_to_block(1); - - // Add right Right1 for Did(0) - // Should succes and trigger the correct event - assert_ok!(Identity::add_right(Origin::root(), 0, Right::Right1)); - let events = System::events(); - assert_eq!(events.len(), 1); +#[test] +fn test_cert_period() { + new_test_ext(DefaultCertificationConfig { + certs_by_issuer: btreemap![0 => btreeset![1]], + phantom: core::marker::PhantomData, + }) + .execute_with(|| { assert_eq!( - events[0], - EventRecord { - phase: Phase::Initialization, - event: Event::Identity(crate::Event::IdtyAcquireRight(Did(0), Right::Right1)), - topics: vec![], - } + DefaultCertification::add_cert(Origin::root(), 0, 2), + Err(Error::<Test, _>::NotRespectCertPeriod.into()) ); - // Add right Right2 for Did(0) - // Should fail because Did(0) already have this right - assert_err!( - Identity::add_right(Origin::root(), 0, Right::Right2), - Error::<Test>::RightAlreadyAdded + run_to_block(CertPeriod::get()); + assert_ok!(DefaultCertification::add_cert(Origin::root(), 0, 2)); + run_to_block(CertPeriod::get() + 1); + assert_eq!( + DefaultCertification::add_cert(Origin::root(), 0, 3), + Err(Error::<Test, _>::NotRespectCertPeriod.into()) ); + run_to_block((2 * CertPeriod::get()) + 1); + assert_ok!(DefaultCertification::add_cert(Origin::root(), 0, 3)); + }); +} - run_to_block(3); - - // Delete right Right1 for Did(1) - // Should succes and trigger the correct event - assert_ok!(Identity::del_right(Origin::root(), 1, Right::Right1)); - let events = System::events(); - assert_eq!(events.len(), 2); +#[test] +fn test_renewable_period() { + new_test_ext(DefaultCertificationConfig { + certs_by_issuer: btreemap![0 => btreeset![1]], + phantom: core::marker::PhantomData, + }) + .execute_with(|| { + run_to_block(CertPeriod::get()); assert_eq!( - events[1], - EventRecord { - phase: Phase::Initialization, - event: Event::Identity(crate::Event::IdtyLostRight(Did(1), Right::Right1)), - topics: vec![], - } + DefaultCertification::add_cert(Origin::root(), 0, 1), + Err(Error::<Test, _>::NotRespectRenewablePeriod.into()) ); - - // The Did(1) identity has no more rights, the inactivity period must start to run - let idty2 = Identity::identity(1); - assert!(idty2.rights.is_empty()); - assert_eq!(idty2.removable_on, Some(7)); + run_to_block(RenewablePeriod::get()); + assert_ok!(DefaultCertification::add_cert(Origin::root(), 0, 1)); + run_to_block(RenewablePeriod::get() + CertPeriod::get()); + assert_eq!( + DefaultCertification::add_cert(Origin::root(), 0, 1), + Err(Error::<Test, _>::NotRespectRenewablePeriod.into()) + ); + run_to_block((2 * RenewablePeriod::get()) + 1); + assert_ok!(DefaultCertification::add_cert(Origin::root(), 0, 1)); }); } -*/ \ No newline at end of file diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs index 7e5d88833c4cb3fbf1fa2004c52e46c6877322fc..d1e9f52a3f9ebd9d256e184de0194c6d17a0f49e 100644 --- a/pallets/identity/src/lib.rs +++ b/pallets/identity/src/lib.rs @@ -60,7 +60,7 @@ pub mod pallet { /// Management of the authorizations of the different calls. (The default implementation only allows root) type EnsureIdtyCallAllowed: EnsureIdtyCallAllowed<Self>; /// Identity custom data - type IdtyData: IdtyData; + type IdtyData: Parameter + Member + MaybeSerializeDeserialize + Debug + Default; /// Identity decentralized identifier type IdtyDid: IdtyDid; /// A short identity index. @@ -78,17 +78,18 @@ pub mod pallet { /// Rights that an identity can have type IdtyRight: IdtyRight; /// On identity confirmed by it's owner - type OnIdtyConfirmed: OnIdtyConfirmed<Self>; - /// On identity removed - type OnIdtyRemoved: OnIdtyRemoved<Self>; - /// On identity validated - type OnIdtyValidated: OnIdtyValidated<Self>; + type OnIdtyChange: OnIdtyChange<Self>; /// On right key change type OnRightKeyChange: OnRightKeyChange<Self>; #[pallet::constant] /// Maximum period of inactivity, after this period, the identity is permanently deleted type MaxInactivityPeriod: Get<Self::BlockNumber>; #[pallet::constant] + /// Maximum period with no rights, after this period, the identity is permanently deleted + type MaxNoRightPeriod: Get<Self::BlockNumber>; + /// Duration after which an identity is renewable + type RenewablePeriod: Get<Self::BlockNumber>; + #[pallet::constant] /// Period after which a non-validated identity is deleted type ValidationPeriod: Get<Self::BlockNumber>; } @@ -118,6 +119,7 @@ pub mod pallet { Created, ConfirmedByOwner, Validated, + Expired, } impl Default for IdtyStatus { fn default() -> Self { @@ -129,34 +131,23 @@ pub mod pallet { #[derive(Encode, Decode, Clone, PartialEq, Eq)] pub struct IdtyValue<T: Config> { pub did: T::IdtyDid, + pub expire_on: T::BlockNumber, pub owner_key: T::AccountId, - pub removable_on: Option<T::BlockNumber>, + pub removable_on: T::BlockNumber, + pub renewable_on: T::BlockNumber, pub rights: Vec<(T::IdtyRight, Option<T::AccountId>)>, pub status: IdtyStatus, pub data: T::IdtyData, } - impl<T: Config> IdtyValue<T> { - pub fn new_valid( - did: T::IdtyDid, - owner_key: T::AccountId, - rights: Vec<T::IdtyRight>, - ) -> Self { - Self { - did, - owner_key, - removable_on: None, - rights: rights.into_iter().map(|right| (right, None)).collect(), - status: IdtyStatus::Validated, - data: T::IdtyData::default(), - } - } - } impl<T: Config> Default for IdtyValue<T> { fn default() -> Self { Self { did: Default::default(), + expire_on: frame_system::Pallet::<T>::block_number() + + T::MaxInactivityPeriod::get(), owner_key: Default::default(), - removable_on: None, + removable_on: T::BlockNumber::zero(), + renewable_on: frame_system::Pallet::<T>::block_number() + T::RenewablePeriod::get(), rights: Default::default(), status: Default::default(), data: Default::default(), @@ -167,7 +158,7 @@ pub mod pallet { pub fn get_right_key(&self, right: T::IdtyRight) -> Option<T::AccountId> { if let Ok(index) = self .rights - .binary_search_by(|(right_, _)| right.cmp(right_)) + .binary_search_by(|(right_, _)| right_.cmp(&right)) { if self.rights[index].1.is_some() { self.rights[index].1.clone() @@ -205,6 +196,12 @@ pub mod pallet { #[pallet::getter(fn identities_count)] pub(super) type IdentitiesCount<T: Config> = StorageValue<_, u64, ValueQuery>; + /// Identities by expiration block + #[pallet::storage] + #[pallet::getter(fn expire_on)] + pub type IdentitiesExpireOn<T: Config> = + StorageMap<_, Blake2_128Concat, T::BlockNumber, Vec<T::IdtyIndex>, ValueQuery>; + /// Identities by removed block #[pallet::storage] #[pallet::getter(fn removable_on)] @@ -244,12 +241,12 @@ pub mod pallet { ); if idty_value.status == IdtyStatus::Validated { if idty_value.rights.is_empty() { - assert!(idty_value.removable_on.is_some()); + assert!(idty_value.removable_on > T::BlockNumber::zero()); } else { - assert!(idty_value.removable_on.is_none()); + assert!(idty_value.removable_on == T::BlockNumber::zero()); } } else { - assert!(idty_value.removable_on.is_some()); + assert!(idty_value.removable_on > T::BlockNumber::zero()); assert!(idty_value.rights.is_empty()) } dids.insert(idty_value.did); @@ -263,9 +260,9 @@ pub mod pallet { <IdentitiesCount<T>>::put(self.identities.len() as u64); for idty_value in &identities { let idty_index = Pallet::<T>::get_next_idty_index(); - if let Some(removable_on) = idty_value.removable_on { + if idty_value.removable_on > T::BlockNumber::zero() { <IdentitiesRemovableOn<T>>::append( - removable_on, + idty_value.removable_on, (idty_index, idty_value.status), ) } @@ -279,7 +276,11 @@ pub mod pallet { #[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { fn on_initialize(n: T::BlockNumber) -> Weight { - Self::prune_identities(n) + if n > T::BlockNumber::zero() { + Self::expire_identities(n) + Self::prune_identities(n) + } else { + 0 + } } } @@ -304,9 +305,9 @@ pub mod pallet { /// An identity has been validated /// [idty] IdtyValidated(T::IdtyDid), - /// An identity was declared dead + /// An identity was renewed by it's owner /// [idty] - IdtyDead(T::IdtyDid), + IdtyRenewed(T::IdtyDid), /// An identity has acquired a new right /// [idty, right] IdtyAcquireRight(T::IdtyDid, T::IdtyRight), @@ -337,9 +338,8 @@ pub mod pallet { idty_did: T::IdtyDid, owner_key: T::AccountId, ) -> DispatchResultWithPostInfo { - if !T::EnsureIdtyCallAllowed::create_identity(origin, creator, &idty_did, &owner_key) { - return Err(Error::<T>::IdtyCreationNotAllowed.into()); - } + let idty_data = + T::EnsureIdtyCallAllowed::create_identity(origin, creator, &idty_did, &owner_key)?; if <IdentitiesByDid<T>>::contains_key(&idty_did) { return Err(Error::<T>::IdtyAlreadyExist.into()); } @@ -352,15 +352,20 @@ pub mod pallet { idty_index, IdtyValue { did: idty_did, + expire_on: T::BlockNumber::zero(), owner_key: owner_key.clone(), - removable_on: Some(removable_on), - ..Default::default() + removable_on, + renewable_on: T::BlockNumber::zero(), + rights: Vec::with_capacity(0), + status: IdtyStatus::Created, + data: idty_data, }, ); <IdentitiesByDid<T>>::insert(idty_did, idty_index); IdentitiesRemovableOn::<T>::append(removable_on, (idty_index, IdtyStatus::Created)); Self::inc_identities_counter(); Self::deposit_event(Event::IdtyCreated(idty_did, owner_key)); + T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Created { creator }); Ok(().into()) } #[pallet::weight(0)] @@ -378,18 +383,22 @@ pub mod pallet { } let block_number = frame_system::pallet::Pallet::<T>::block_number(); + let expire_on = block_number + T::MaxInactivityPeriod::get(); let removable_on = block_number + T::ValidationPeriod::get(); - idty_value.removable_on = Some(removable_on); + let renewable_on = block_number + T::RenewablePeriod::get(); + idty_value.expire_on = expire_on; + idty_value.removable_on = removable_on; + idty_value.renewable_on = renewable_on; idty_value.status = IdtyStatus::ConfirmedByOwner; - let owner_key = idty_value.owner_key.clone(); <Identities<T>>::insert(idty_index, idty_value); + IdentitiesExpireOn::<T>::append(expire_on, idty_index); IdentitiesRemovableOn::<T>::append( removable_on, (idty_index, IdtyStatus::ConfirmedByOwner), ); Self::deposit_event(Event::IdtyConfirmed(idty_did)); - T::OnIdtyConfirmed::on_idty_confirmed(idty_did, owner_key, removable_on); + T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Confirmed); Ok(().into()) } else { Err(Error::<T>::RequireToBeOwner.into()) @@ -399,6 +408,48 @@ pub mod pallet { } } #[pallet::weight(0)] + pub fn renew_identity( + origin: OriginFor<T>, + idty_did: T::IdtyDid, + idty_index: T::IdtyIndex, + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + + if let Ok(mut idty_value) = <Identities<T>>::try_get(idty_index) { + if who == idty_value.owner_key { + match idty_value.status { + IdtyStatus::Created | IdtyStatus::ConfirmedByOwner => { + Err(Error::<T>::IdtyNotValidated.into()) + } + IdtyStatus::Validated | IdtyStatus::Expired => { + let block_number = frame_system::pallet::Pallet::<T>::block_number(); + if idty_value.renewable_on > block_number { + return Err(Error::<T>::IdtyNotYetRenewable.into()); + } + let expire_on = block_number + T::MaxInactivityPeriod::get(); + let renewable_on = block_number + T::RenewablePeriod::get(); + idty_value.expire_on = expire_on; + idty_value.renewable_on = renewable_on; + let old_status = idty_value.status; + idty_value.status = IdtyStatus::Validated; + + <Identities<T>>::insert(idty_index, idty_value); + IdentitiesExpireOn::<T>::append(expire_on, idty_index); + Self::deposit_event(Event::IdtyRenewed(idty_did)); + if old_status == IdtyStatus::Expired { + T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Validated); + } + Ok(().into()) + } + } + } else { + Err(Error::<T>::RequireToBeOwner.into()) + } + } else { + Err(Error::<T>::IdtyNotFound.into()) + } + } + #[pallet::weight(0)] pub fn validate_identity( origin: OriginFor<T>, idty_index: T::IdtyIndex, @@ -410,10 +461,9 @@ pub mod pallet { IdtyStatus::Created => Err(Error::<T>::IdtyNotConfirmedByOwner.into()), IdtyStatus::ConfirmedByOwner => { let block_number = frame_system::pallet::Pallet::<T>::block_number(); - let removable_on = block_number + T::MaxInactivityPeriod::get(); - idty_value.removable_on = Some(removable_on); + let removable_on = block_number + T::MaxNoRightPeriod::get(); + idty_value.removable_on = removable_on; idty_value.status = IdtyStatus::Validated; - let owner_key = idty_value.owner_key.clone(); let did = idty_value.did; <Identities<T>>::insert(idty_index, idty_value); @@ -422,10 +472,12 @@ pub mod pallet { (idty_index, IdtyStatus::Validated), ); Self::deposit_event(Event::IdtyValidated(did)); - T::OnIdtyValidated::on_idty_validated(idty_index, owner_key)?; + T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Validated); Ok(().into()) } - IdtyStatus::Validated => Err(Error::<T>::IdtyAlreadyValidated.into()), + IdtyStatus::Validated | IdtyStatus::Expired => { + Err(Error::<T>::IdtyAlreadyValidated.into()) + } } } else { Err(Error::<T>::IdtyNotFound.into()) @@ -443,21 +495,31 @@ pub mod pallet { match idty_value.status { IdtyStatus::Created => Err(Error::<T>::IdtyNotConfirmedByOwner.into()), IdtyStatus::ConfirmedByOwner => { - idty_value.removable_on = None; + idty_value.removable_on = T::BlockNumber::zero(); idty_value.rights = rights.iter().map(|right| (*right, None)).collect(); idty_value.status = IdtyStatus::Validated; - let owner_key = idty_value.owner_key.clone(); let did = idty_value.did; + let owner_key = idty_value.owner_key.clone(); <Identities<T>>::insert(idty_index, idty_value); Self::deposit_event(Event::IdtyValidated(did)); + T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Validated); for right in rights { Self::deposit_event(Event::IdtyAcquireRight(did, right)); + if right.allow_owner_key() { + T::OnRightKeyChange::on_right_key_change( + idty_index, + right, + None, + Some(owner_key.clone()), + ); + } } - T::OnIdtyValidated::on_idty_validated(idty_index, owner_key)?; Ok(().into()) } - IdtyStatus::Validated => Err(Error::<T>::IdtyAlreadyValidated.into()), + IdtyStatus::Validated | IdtyStatus::Expired => { + Err(Error::<T>::IdtyAlreadyValidated.into()) + } } } else { Err(Error::<T>::IdtyNotFound.into()) @@ -478,7 +540,7 @@ pub mod pallet { if let Err(index) = idty_value .rights - .binary_search_by(|(right_, _)| right.cmp(right_)) + .binary_search_by(|(right_, _)| right_.cmp(&right)) { let did = idty_value.did; let new_key = if right.allow_owner_key() { @@ -487,7 +549,7 @@ pub mod pallet { None }; - idty_value.removable_on = None; + idty_value.removable_on = T::BlockNumber::zero(); idty_value.rights.insert(index, (right, None)); <Identities<T>>::insert(idty_index, idty_value); Self::deposit_event(Event::<T>::IdtyAcquireRight(did, right)); @@ -517,7 +579,7 @@ pub mod pallet { if let Ok(index) = idty_value .rights - .binary_search_by(|(right_, _)| right.cmp(right_)) + .binary_search_by(|(right_, _)| right_.cmp(&right)) { let did = idty_value.did; let old_key_opt = if let Some(ref subkey) = idty_value.rights[index].1 { @@ -531,8 +593,8 @@ pub mod pallet { if idty_value.rights.is_empty() { let block_number = frame_system::pallet::Pallet::<T>::block_number(); - let removable_on = block_number + T::MaxInactivityPeriod::get(); - idty_value.removable_on = Some(removable_on); + let removable_on = block_number + T::MaxNoRightPeriod::get(); + idty_value.removable_on = removable_on; <IdentitiesRemovableOn<T>>::append( removable_on, (idty_index, IdtyStatus::Validated), @@ -574,7 +636,7 @@ pub mod pallet { if let Ok(index) = idty_value .rights - .binary_search_by(|(right_, _)| right.cmp(right_)) + .binary_search_by(|(right_, _)| right_.cmp(&right)) { let did = idty_value.did; let old_subkey_opt = idty_value.rights[index].1.clone(); @@ -631,6 +693,8 @@ pub mod pallet { IdtyNotFound, /// Identity not validated IdtyNotValidated, + /// Identity not yet renewable + IdtyNotYetRenewable, /// This operation requires to be the owner of the identity RequireToBeOwner, /// Right already added @@ -639,6 +703,16 @@ pub mod pallet { RightNotExist, } + // PUBLIC FUNCTIONS // + + impl<T: Config> Pallet<T> { + pub fn set_idty_data(idty_index: T::IdtyIndex, idty_data: T::IdtyData) { + Identities::<T>::mutate_exists(idty_index, |idty_val| { + idty_val.get_or_insert(IdtyValue::default()).data = idty_data; + }); + } + } + // INTERNAL FUNCTIONS // impl<T: Config> Pallet<T> { @@ -654,8 +728,8 @@ pub mod pallet { <NextIdtyIndex<T>>::put(next_index.saturating_add(T::IdtyIndex::one())); next_index } else { - <NextIdtyIndex<T>>::put(T::IdtyIndex::one()); - T::IdtyIndex::zero() + <NextIdtyIndex<T>>::put(T::IdtyIndex::one() + T::IdtyIndex::one()); + T::IdtyIndex::one() } } fn inc_identities_counter() { @@ -665,6 +739,31 @@ pub mod pallet { <IdentitiesCount<T>>::put(1); } } + fn expire_identities(block_number: T::BlockNumber) -> Weight { + let mut total_weight: Weight = 0; + + use frame_support::storage::generator::StorageMap as _; + if let Some(identities_index) = IdentitiesExpireOn::<T>::from_query_to_optional_value( + IdentitiesExpireOn::<T>::take(block_number), + ) { + for idty_index in identities_index { + if let Ok(idty_val) = <Identities<T>>::try_get(idty_index) { + if idty_val.expire_on == block_number { + <Identities<T>>::mutate_exists(idty_index, |idty_val_opt| { + idty_val_opt.get_or_insert(IdtyValue::default()).rights = + Vec::with_capacity(0); + idty_val_opt.get_or_insert(IdtyValue::default()).status = + IdtyStatus::Expired; + }); + total_weight += + T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Expired); + } + } + } + } + + total_weight + } fn prune_identities(block_number: T::BlockNumber) -> Weight { let mut total_weight: Weight = 0; @@ -674,18 +773,13 @@ pub mod pallet { ) { for (idty_index, idty_status) in identities { if let Ok(idty_val) = <Identities<T>>::try_get(idty_index) { - if idty_val.removable_on == Some(block_number) - && idty_val.status == idty_status - { + if idty_val.removable_on == block_number && idty_val.status == idty_status { let did = idty_val.did; <Identities<T>>::remove(idty_index); <IdentitiesByDid<T>>::remove(did); Self::dec_identities_counter(); - total_weight += T::OnIdtyRemoved::on_idty_removed( - idty_index, - did, - idty_val.owner_key, - ); + total_weight += + T::OnIdtyChange::on_idty_change(idty_index, IdtyEvent::Removed); } } } diff --git a/pallets/identity/src/mock.rs b/pallets/identity/src/mock.rs index c8973aaad3dda9388f69f140ddcd050868ea0f3f..47310db1e9a12ad242180d6ab18bf239293167a6 100644 --- a/pallets/identity/src/mock.rs +++ b/pallets/identity/src/mock.rs @@ -101,7 +101,9 @@ impl system::Config for Test { parameter_types! { pub const ConfirmPeriod: u64 = 2; - pub const MaxInactivityPeriod: u64 = 4; + pub const MaxInactivityPeriod: u64 = 5; + pub const MaxNoRightPeriod: u64 = 4; + pub const RenewablePeriod: u64 = 3; pub const ValidationPeriod: u64 = 2; } @@ -116,11 +118,11 @@ impl pallet_identity::Config for Test { type IdtyIndex = u64; type IdtyValidationOrigin = system::EnsureRoot<AccountId>; type IdtyRight = IdtyRight; - type OnIdtyConfirmed = (); - type OnIdtyRemoved = (); - type OnIdtyValidated = (); + type OnIdtyChange = (); type OnRightKeyChange = (); type MaxInactivityPeriod = MaxInactivityPeriod; + type MaxNoRightPeriod = MaxNoRightPeriod; + type RenewablePeriod = RenewablePeriod; type ValidationPeriod = ValidationPeriod; } diff --git a/pallets/identity/src/tests.rs b/pallets/identity/src/tests.rs index ba8efc3fe27fc92b15c496e694e633b929ae9292..b40d194edefefaef82d8bd818a7da56c261496da 100644 --- a/pallets/identity/src/tests.rs +++ b/pallets/identity/src/tests.rs @@ -35,16 +35,20 @@ fn test_two_identities() { let identities = vec![ crate::IdtyValue { did: Did(0), + expire_on: 5, owner_key: 1, - removable_on: None, + removable_on: 0, + renewable_on: 3, rights: vec![(Right::Right2, Some(10))], status: crate::IdtyStatus::Validated, data: (), }, crate::IdtyValue { did: Did(1), + expire_on: 5, owner_key: 2, - removable_on: None, + removable_on: 0, + renewable_on: 3, rights: vec![(Right::Right1, Some(20))], status: crate::IdtyStatus::Validated, data: (), @@ -97,6 +101,6 @@ fn test_two_identities() { // The Did(1) identity has no more rights, the inactivity period must start to run let idty2 = Identity::identity(1); assert!(idty2.rights.is_empty()); - assert_eq!(idty2.removable_on, Some(7)); + assert_eq!(idty2.removable_on, 7); }); } diff --git a/pallets/identity/src/traits.rs b/pallets/identity/src/traits.rs index 92dba5318f4d097784851b4b450dcf6cc0e733ed..a61a475e2a65d12eec1aa56172a681d625ff5bae 100644 --- a/pallets/identity/src/traits.rs +++ b/pallets/identity/src/traits.rs @@ -15,7 +15,7 @@ // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. use crate::*; -use frame_support::pallet_prelude::*; +use frame_support::{dispatch::DispatchError, pallet_prelude::*}; use frame_system::pallet_prelude::*; use sp_runtime::traits::MaybeSerializeDeserialize; use sp_std::fmt::Debug; @@ -26,7 +26,7 @@ pub trait EnsureIdtyCallAllowed<T: Config> { creator: T::IdtyIndex, idty_did: &T::IdtyDid, idty_owner_key: &T::AccountId, - ) -> bool; + ) -> Result<T::IdtyData, DispatchError>; } impl<T: Config> EnsureIdtyCallAllowed<T> for () { fn create_identity( @@ -34,21 +34,14 @@ impl<T: Config> EnsureIdtyCallAllowed<T> for () { _creator: T::IdtyIndex, _idty_did: &T::IdtyDid, _idty_owner_key: &T::AccountId, - ) -> bool { - ensure_root(origin).is_ok() + ) -> Result<T::IdtyData, DispatchError> { + match ensure_root(origin) { + Ok(()) => Ok(T::IdtyData::default()), + Err(_) => Err(DispatchError::BadOrigin), + } } } -pub trait IdtyData: - frame_support::Parameter - + frame_support::pallet_prelude::Member - + MaybeSerializeDeserialize - + Debug - + Default -{ -} -impl IdtyData for () {} - pub trait IdtyDid: frame_support::Parameter + frame_support::pallet_prelude::Member @@ -72,50 +65,19 @@ pub trait IdtyRight: fn allow_owner_key(self) -> bool; } -pub trait OnIdtyConfirmed<T: Config> { - fn on_idty_confirmed( - idty_did: T::IdtyDid, - owner_key: T::AccountId, - removable_on: T::BlockNumber, - ); -} -impl<T: Config> OnIdtyConfirmed<T> for () { - fn on_idty_confirmed( - _idty_did: T::IdtyDid, - _owner_key: T::AccountId, - _removable_on: T::BlockNumber, - ) { - } -} - -pub trait OnIdtyValidated<T: Config> { - fn on_idty_validated( - idty_index: T::IdtyIndex, - owner_key: T::AccountId, - ) -> DispatchResultWithPostInfo; -} -impl<T: Config> OnIdtyValidated<T> for () { - fn on_idty_validated( - _idty_index: T::IdtyIndex, - _owner_key: T::AccountId, - ) -> DispatchResultWithPostInfo { - Ok(().into()) - } +pub enum IdtyEvent<T: Config> { + Created { creator: T::IdtyIndex }, + Confirmed, + Validated, + Expired, + Removed, } -pub trait OnIdtyRemoved<T: Config> { - fn on_idty_removed( - idty_index: T::IdtyIndex, - idty_did: T::IdtyDid, - owner_key: T::AccountId, - ) -> Weight; +pub trait OnIdtyChange<T: Config> { + fn on_idty_change(idty_index: T::IdtyIndex, idty_event: IdtyEvent<T>) -> Weight; } -impl<T: Config> OnIdtyRemoved<T> for () { - fn on_idty_removed( - _idty_index: T::IdtyIndex, - _idty_did: T::IdtyDid, - _owner_key: T::AccountId, - ) -> Weight { +impl<T: Config> OnIdtyChange<T> for () { + fn on_idty_change(_idty_index: T::IdtyIndex, _idty_event: IdtyEvent<T>) -> Weight { 0 } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 1ea14d770defb9ea24bd2f1aa2a2bf94cc2ebc30..6787d2e025211625b66672d2b6b8a433cc38470f 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -35,6 +35,8 @@ std = [ 'frame-system/std', 'pallet-aura/std', 'pallet-balances/std', + 'pallet-certification/std', + 'pallet-identity/std', 'pallet-grandpa/std', 'pallet-randomness-collective-flip/std', 'pallet-sudo/std', @@ -116,6 +118,10 @@ git = 'https://github.com/paritytech/substrate.git' tag = 'monthly-2021-07' version = '3.0.0' +[dependencies.pallet-certification] +default-features = false +path = '../pallets/certification' + [dependencies.pallet-grandpa] default-features = false git = 'https://github.com/paritytech/substrate.git' diff --git a/runtime/src/authorizations.rs b/runtime/src/authorizations.rs index d293b1f6666a806452f7bb5002827ef66e2c7e54..b8c04d959278569f528a14829473748db2c2e5fe 100644 --- a/runtime/src/authorizations.rs +++ b/runtime/src/authorizations.rs @@ -14,7 +14,15 @@ // You should have received a copy of the GNU Affero General Public License // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. -use crate::{AccountId, Identity, IdtyDid, IdtyIndex, IdtyRight, Origin, Runtime}; +use crate::{ + AccountId, BlockNumber, Identity, IdtyData, IdtyDid, IdtyIndex, IdtyRight, Origin, Runtime, + StrongCert, System, +}; +use frame_support::pallet_prelude::DispatchError; +use frame_support::traits::EnsureOrigin; +use pallet_identity::IdtyStatus; + +const IDTY_CREATE_PERIOD: BlockNumber = 100; pub struct EnsureIdtyCallAllowedImpl; impl pallet_identity::traits::EnsureIdtyCallAllowed<Runtime> for EnsureIdtyCallAllowedImpl { @@ -23,19 +31,89 @@ impl pallet_identity::traits::EnsureIdtyCallAllowed<Runtime> for EnsureIdtyCallA creator: IdtyIndex, _idty_did: &IdtyDid, _idty_owner_key: &AccountId, - ) -> bool { + ) -> Result<IdtyData, DispatchError> { + let block_number = System::block_number(); + let creator_idty_data = IdtyData { + can_create_on: block_number + IDTY_CREATE_PERIOD, + }; + let new_idty_data = IdtyData { can_create_on: 0 }; match origin.into() { - Ok(frame_system::RawOrigin::Root) => true, + Ok(frame_system::RawOrigin::Root) => { + Identity::set_idty_data(creator, creator_idty_data); + Ok(new_idty_data) + } Ok(frame_system::RawOrigin::Signed(signer)) => { let creator_idty = Identity::identity(creator); if let Some(authorized_key) = creator_idty.get_right_key(IdtyRight::CreateIdty) { - signer == authorized_key + if signer != authorized_key { + frame_support::runtime_print!("signer != authorized_key"); + Err(DispatchError::Other("signer != authorized_key")) + } else if !StrongCert::is_idty_allowed_to_create_cert(creator) { + frame_support::runtime_print!("not allowed to create cert"); + Err(DispatchError::Other("not allowed to create cert")) + } else if creator_idty.data.can_create_on > System::block_number() { + frame_support::runtime_print!("Not respect IdtyCreatePeriod"); + Err(DispatchError::Other("Not respect IdtyCreatePeriod")) + } else { + Identity::set_idty_data(creator, creator_idty_data); + Ok(new_idty_data) + } + } else { + frame_support::runtime_print!("Idty not have right CreateIdty"); + Err(DispatchError::Other("Idty not have right CreateIdty")) + } + } + _ => { + frame_support::runtime_print!("Origin neither root or signed"); + Err(DispatchError::Other("Origin neither root or signed")) + } + } + } +} + +pub struct AddStrongCertOrigin; +impl EnsureOrigin<(Origin, IdtyIndex, IdtyIndex)> for AddStrongCertOrigin { + type Success = (); + + fn try_origin( + o: (Origin, IdtyIndex, IdtyIndex), + ) -> Result<Self::Success, (Origin, IdtyIndex, IdtyIndex)> { + match o.0.clone().into() { + Ok(frame_system::RawOrigin::Root) => Ok(()), + Ok(frame_system::RawOrigin::Signed(who)) => { + let issuer = Identity::identity(o.1); + if let Some(allowed_key) = issuer.get_right_key(IdtyRight::StrongCert) { + if who == allowed_key { + let receiver = Identity::identity(o.2); + match receiver.status { + IdtyStatus::ConfirmedByOwner | IdtyStatus::Validated => Ok(()), + IdtyStatus::Created | IdtyStatus::Expired => Err(o), + } + } else { + // Bad key + Err(o) + } } else { - false + // Issuer has not right StrongCert + Err(o) } } - _ => false, + _ => Err(o), + } + } +} + +pub struct DelStrongCertOrigin; +impl EnsureOrigin<(Origin, IdtyIndex, IdtyIndex)> for DelStrongCertOrigin { + type Success = (); + + fn try_origin( + o: (Origin, IdtyIndex, IdtyIndex), + ) -> Result<Self::Success, (Origin, IdtyIndex, IdtyIndex)> { + match o.0.clone().into() { + Ok(frame_system::RawOrigin::Root) => Ok(()), + _ => Err(o), } } } diff --git a/runtime/src/entities.rs b/runtime/src/entities.rs index d60d8b4086cf15f780b8e0ed3f25c772d0def88c..ba9b8f91ff9fd973a4717f04e5a5ff21467c27e8 100644 --- a/runtime/src/entities.rs +++ b/runtime/src/entities.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. +use crate::BlockNumber; use frame_support::pallet_prelude::*; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; @@ -35,8 +36,8 @@ impl Default for IdtyRight { impl pallet_identity::traits::IdtyRight for IdtyRight { fn allow_owner_key(self) -> bool { match self { - Self::CreateIdty | Self::LightCert | Self::Ud => true, - IdtyRight::StrongCert => false, + Self::CreateIdty | Self::LightCert | IdtyRight::StrongCert | Self::Ud => true, + //IdtyRight::StrongCert => false, //_ => false, } } @@ -84,6 +85,12 @@ impl Ord for IdtyDid { } impl pallet_identity::traits::IdtyDid for IdtyDid {} +#[cfg_attr(feature = "std", derive(Deserialize, Serialize))] +#[derive(Encode, Decode, Default, Clone, Copy, PartialEq, Eq, RuntimeDebug)] +pub struct IdtyData { + pub can_create_on: BlockNumber, +} + #[cfg_attr(feature = "std", derive(Deserialize, Serialize))] #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] pub enum Planet { diff --git a/runtime/src/handlers.rs b/runtime/src/handlers.rs index 2eaab7ed1be1fc5b5027f973e9101936b6743a13..04ad4d9f5e0ee080154a6b50c0bec0defd10c514 100644 --- a/runtime/src/handlers.rs +++ b/runtime/src/handlers.rs @@ -14,16 +14,30 @@ // You should have received a copy of the GNU Affero General Public License // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>. -use super::{AccountId, Identity, IdtyIndex, IdtyRight, Origin, Runtime, UdAccountsStorage}; -use frame_support::pallet_prelude::DispatchResultWithPostInfo; +use super::{ + AccountId, Identity, IdtyIndex, IdtyRight, Origin, Runtime, StrongCert, UdAccountsStorage, + Weight, +}; +use pallet_identity::traits::IdtyEvent; -pub struct OnIdtyValidatedHandler; -impl pallet_identity::traits::OnIdtyValidated<Runtime> for OnIdtyValidatedHandler { - fn on_idty_validated( - idty_index: IdtyIndex, - _owner_key: AccountId, - ) -> DispatchResultWithPostInfo { - Identity::add_right(Origin::root(), idty_index, IdtyRight::Ud) +const MIN_STRONG_CERT_FOR_UD: u32 = 2; +const MIN_STRONG_CERT_FOR_STRONG_CERT: u32 = 3; + +pub struct OnIdtyChangeHandler; +impl pallet_identity::traits::OnIdtyChange<Runtime> for OnIdtyChangeHandler { + fn on_idty_change(idty_index: IdtyIndex, idty_event: IdtyEvent<Runtime>) -> Weight { + let total_weight = 0; + match idty_event { + IdtyEvent::Created { creator } => { + // totad_weight += StrongCert::WeightInfo::add_cert(); + let _ = StrongCert::add_cert(Origin::root(), creator, idty_index); + } + IdtyEvent::Confirmed => {} + IdtyEvent::Validated => {} + IdtyEvent::Expired => {} + IdtyEvent::Removed => {} + }; + total_weight } } @@ -43,3 +57,49 @@ impl pallet_identity::traits::OnRightKeyChange<Runtime> for OnRightKeyChangeHand }; } } + +pub struct OnNewStrongCertHandler; +impl pallet_certification::traits::OnNewcert<IdtyIndex> for OnNewStrongCertHandler { + fn on_new_cert( + _issuer: IdtyIndex, + _issuer_issued_count: u8, + receiver: IdtyIndex, + receiver_received_count: u32, + ) -> frame_support::dispatch::Weight { + let total_weight = 0; + match receiver_received_count { + MIN_STRONG_CERT_FOR_UD => { + // total_weight += Identity::WeightInfo::add_right(); + let _ = Identity::validate_identity_and_add_rights( + Origin::root(), + receiver, + sp_std::vec![IdtyRight::Ud], + ); + } + MIN_STRONG_CERT_FOR_STRONG_CERT => { + // total_weight += Identity::WeightInfo::add_right(); + let _ = Identity::add_right(Origin::root(), receiver, IdtyRight::StrongCert); + } + _ => {} + } + total_weight + } +} + +pub struct OnRemovedStrongCertHandler; +impl pallet_certification::traits::OnRemovedCert<IdtyIndex> for OnRemovedStrongCertHandler { + fn on_removed_cert( + _issuer: IdtyIndex, + _issuer_issued_count: u8, + receiver: IdtyIndex, + receiver_received_count: u32, + _expiration: bool, + ) -> frame_support::dispatch::Weight { + let total_weight = 0; + if receiver_received_count < MIN_STRONG_CERT_FOR_UD { + // total_weight += Identity::WeightInfo::del_right(); + let _ = Identity::del_right(Origin::root(), receiver, IdtyRight::Ud); + } + total_weight + } +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2e3423d19f9e4ed8edfd53969ceb11e4827334cf..5f5e8bcc19c02082ef8eedb521a25ef00ae70787 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -26,9 +26,9 @@ mod authorizations; mod entities; mod handlers; -pub use crate::entities::{IdtyDid, IdtyRight, Planet}; +pub use crate::entities::{IdtyData, IdtyDid, IdtyRight, Planet}; pub use pallet_balances::Call as BalancesCall; -pub use pallet_identity::IdtyValue; +pub use pallet_identity::{IdtyStatus, IdtyValue}; pub use pallet_timestamp::Call as TimestampCall; use pallet_transaction_payment::CurrencyAdapter; pub use pallet_universal_dividend; @@ -140,6 +140,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { /// /// Change this to adjust the block time. pub const MILLISECS_PER_BLOCK: u64 = 6000; +pub const SECS_PER_BLOCK: u64 = MILLISECS_PER_BLOCK / 1_000; // NOTE: Currently it is not possible to change the slot duration after the chain has started. // Attempting to do so will brick block production. @@ -149,8 +150,9 @@ pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; -pub const MONTHS: BlockNumber = DAYS * 30; -pub const YEARS: BlockNumber = MONTHS * 12; +const SECS_PER_YEAR: u64 = 31_557_600; // (365.25 * 24 * 60 * 60) +pub const MONTHS: BlockNumber = (SECS_PER_YEAR / (12 * SECS_PER_BLOCK)) as BlockNumber; +pub const YEARS: BlockNumber = (SECS_PER_YEAR / SECS_PER_BLOCK) as BlockNumber; /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] @@ -321,7 +323,9 @@ impl pallet_sudo::Config for Runtime { parameter_types! { pub const ConfirmPeriod: BlockNumber = 12 * HOURS; - pub const MaxInactivityPeriod: BlockNumber = 1 * YEARS; + pub const MaxInactivityPeriod: BlockNumber = YEARS; + pub const MaxNoRightPeriod: BlockNumber = YEARS; + pub const IdtyRenewablePeriod: BlockNumber = 6 * MONTHS; pub const ValidationPeriod: BlockNumber = 2 * MONTHS; } @@ -332,19 +336,42 @@ impl pallet_identity::Config for Runtime { type AddRightOrigin = EnsureRoot<Self::AccountId>; type DelRightOrigin = EnsureRoot<Self::AccountId>; type EnsureIdtyCallAllowed = crate::authorizations::EnsureIdtyCallAllowedImpl; - type IdtyData = (); + type IdtyData = IdtyData; type IdtyDid = IdtyDid; type IdtyIndex = IdtyIndex; type IdtyValidationOrigin = EnsureRoot<Self::AccountId>; type IdtyRight = IdtyRight; - type OnIdtyConfirmed = (); - type OnIdtyRemoved = (); - type OnIdtyValidated = crate::handlers::OnIdtyValidatedHandler; + type OnIdtyChange = crate::handlers::OnIdtyChangeHandler; type OnRightKeyChange = OnRightKeyChangeHandler; type MaxInactivityPeriod = MaxInactivityPeriod; + type MaxNoRightPeriod = MaxNoRightPeriod; + type RenewablePeriod = IdtyRenewablePeriod; type ValidationPeriod = ValidationPeriod; } +// PALLET CERTIFICATION + +parameter_types! { + pub const CertPeriod: BlockNumber = 15; + pub const MaxByIssuer: u8 = 100; + pub const StrongCertRenewablePeriod: BlockNumber = 50;//6 * MONTHS; + pub const ValidityPeriod: BlockNumber = 200;//2 * YEARS; +} + +/// Configure the pallet certification +impl pallet_certification::Config for Runtime { + type AddCertOrigin = crate::authorizations::AddStrongCertOrigin; + type CertPeriod = CertPeriod; + type DelCertOrigin = crate::authorizations::DelStrongCertOrigin; + type Event = Event; + type IdtyIndex = IdtyIndex; + type MaxByIssuer = MaxByIssuer; + type OnNewcert = crate::handlers::OnNewStrongCertHandler; + type OnRemovedCert = crate::handlers::OnRemovedStrongCertHandler; + type RenewablePeriod = StrongCertRenewablePeriod; + type ValidityPeriod = ValidityPeriod; +} + // PALLET UNIVERSAL DIVIDEND parameter_types! { @@ -365,7 +392,7 @@ impl Get<Vec<AccountId>> for UdAccountsProvider { /// Configure the pallet universal-dividend in pallets/universal-dividend. impl pallet_universal_dividend::Config for Runtime { - const UD_CREATION_PERIOD: Self::BlockNumber = 10; + const UD_CREATION_PERIOD: Self::BlockNumber = 20; const UD_REEVAL_PERIOD: Balance = 10; const UD_REEVAL_PERIOD_IN_BLOCKS: Self::BlockNumber = Self::UD_CREATION_PERIOD * Self::UD_REEVAL_PERIOD as Self::BlockNumber; @@ -398,6 +425,7 @@ construct_runtime!( UdAccountsStorage: pallet_ud_accounts_storage::{Pallet, Config<T>, Storage}, UniversalDividend: pallet_universal_dividend::{Pallet, Config<T>, Storage, Event<T>}, Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>}, + StrongCert: pallet_certification::{Pallet, Call, Config<T>, Storage, Event<T>}, } ); diff --git a/types-bundle/types_definition.json b/types-bundle/types_definition.json index defb1c71896dd263841348742d7090ec59928664..d9d3f34bfac2deb48c78402f489d48a84cb77067 100644 --- a/types-bundle/types_definition.json +++ b/types-bundle/types_definition.json @@ -1,9 +1,16 @@ { "Balance": "u64", - "Planet": { - "_enum": [ - "Earth" - ] + "CertValue": { + "chainable_on": "BlockNumber", + "removable_on": "BlockNumber" + }, + "IdtyCertMeta": { + "issued_count": "u8", + "next_issuable_on": "BlockNumber", + "received_count": "u32" + }, + "IdtyData": { + "can_create_on": "BlockNumber" }, "IdtyCertMeta": { "issued_count": "u8", @@ -34,9 +41,17 @@ }, "IdtyValue": { "did": "IdtyDid", + "expire_on": "BlockNumber", "owner_key": "AccountId", - "removable_on": "Option<BlockNumber>", + "removable_on": "BlockNumber", + "renewable_on": "BlockNumber", "rights": "Vec<(T::IdtyRight, Option<T::AccountId>)>", "status": "IdtyStatus", + "data": "IdtyData" + }, + "Planet": { + "_enum": [ + "Earth" + ] } }