Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • nodes/rust/duniter-v2s
  • llaq/lc-core-substrate
  • pini-gh/duniter-v2s
  • vincentux/duniter-v2s
  • mildred/duniter-v2s
  • d0p1/duniter-v2s
  • bgallois/duniter-v2s
  • Nicolas80/duniter-v2s
8 results
Show changes
Showing
with 223 additions and 9 deletions
...@@ -269,6 +269,7 @@ pub mod pallet { ...@@ -269,6 +269,7 @@ pub mod pallet {
#[pallet::call] #[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> { impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// add a certification without checks (only root)
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
pub fn force_add_cert( pub fn force_add_cert(
origin: OriginFor<T>, origin: OriginFor<T>,
...@@ -355,6 +356,7 @@ pub mod pallet { ...@@ -355,6 +356,7 @@ pub mod pallet {
Self::do_add_cert(block_number, issuer, receiver) Self::do_add_cert(block_number, issuer, receiver)
} }
/// remove a certification (only root)
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
pub fn del_cert( pub fn del_cert(
origin: OriginFor<T>, origin: OriginFor<T>,
...@@ -366,6 +368,7 @@ pub mod pallet { ...@@ -366,6 +368,7 @@ pub mod pallet {
Ok(().into()) Ok(().into())
} }
/// remove all certifications received by an identity (only root)
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
pub fn remove_all_certs_received_by( pub fn remove_all_certs_received_by(
origin: OriginFor<T>, origin: OriginFor<T>,
...@@ -382,6 +385,7 @@ pub mod pallet { ...@@ -382,6 +385,7 @@ pub mod pallet {
// INTERNAL FUNCTIONS // // INTERNAL FUNCTIONS //
impl<T: Config<I>, I: 'static> Pallet<T, I> { impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// perform cert add
fn do_add_cert( fn do_add_cert(
block_number: T::BlockNumber, block_number: T::BlockNumber,
issuer: T::IdtyIndex, issuer: T::IdtyIndex,
...@@ -438,6 +442,7 @@ pub mod pallet { ...@@ -438,6 +442,7 @@ pub mod pallet {
Ok(().into()) Ok(().into())
} }
/// remove the certifications due to expire on the given block
fn prune_certifications(block_number: T::BlockNumber) -> Weight { fn prune_certifications(block_number: T::BlockNumber) -> Weight {
let mut total_weight = Weight::zero(); let mut total_weight = Weight::zero();
...@@ -449,6 +454,7 @@ pub mod pallet { ...@@ -449,6 +454,7 @@ pub mod pallet {
total_weight total_weight
} }
/// perform the certification removal
fn remove_cert_inner( fn remove_cert_inner(
issuer: T::IdtyIndex, issuer: T::IdtyIndex,
receiver: T::IdtyIndex, receiver: T::IdtyIndex,
...@@ -505,6 +511,7 @@ pub mod pallet { ...@@ -505,6 +511,7 @@ pub mod pallet {
} }
} }
// implement setting next_issuable_on for certification period
impl<T: Config<I>, I: 'static> SetNextIssuableOn<T::BlockNumber, T::IdtyIndex> for Pallet<T, I> { impl<T: Config<I>, I: 'static> SetNextIssuableOn<T::BlockNumber, T::IdtyIndex> for Pallet<T, I> {
fn set_next_issuable_on(idty_index: T::IdtyIndex, next_issuable_on: T::BlockNumber) -> Weight { fn set_next_issuable_on(idty_index: T::IdtyIndex, next_issuable_on: T::BlockNumber) -> Weight {
<StorageIdtyCertMeta<T, I>>::mutate_exists(idty_index, |cert_meta_opt| { <StorageIdtyCertMeta<T, I>>::mutate_exists(idty_index, |cert_meta_opt| {
......
...@@ -20,10 +20,14 @@ use codec::{Decode, Encode}; ...@@ -20,10 +20,14 @@ use codec::{Decode, Encode};
use frame_support::pallet_prelude::*; use frame_support::pallet_prelude::*;
use scale_info::TypeInfo; use scale_info::TypeInfo;
/// certification metadata attached to an identity
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct IdtyCertMeta<BlockNumber: Default> { pub struct IdtyCertMeta<BlockNumber: Default> {
/// count issued certifications
pub issued_count: u32, pub issued_count: u32,
/// block before which identity not allowed to issue a new certification
pub next_issuable_on: BlockNumber, pub next_issuable_on: BlockNumber,
/// number of certifications received
pub received_count: u32, pub received_count: u32,
} }
......
# Duniter account pallet
Duniter customizes the `AccountData` of the `Balances` Substrate pallet. In particular, it adds a field `RandomId`.
## RandomID
The RandomId field was added with the idea to provide a unique id that can not be controlled by user to serve as a basis for robust identification. The discussion is available on the forum.
https://forum.duniter.org/t/la-solution-pour-des-identicones-securisees-le-random-id/9126
## Account creation fee
DuniterAccount defines a creation fee that is preleved to the account one block after its creation. This fee goes to the treasury.
## Sufficient
DuniterAccount tweaks the substrate AccountInfo to allow identity accounts to exist without existential deposit. This allows to spare the creation fee.
\ No newline at end of file
...@@ -20,11 +20,17 @@ use scale_info::TypeInfo; ...@@ -20,11 +20,17 @@ use scale_info::TypeInfo;
use sp_core::H256; use sp_core::H256;
use sp_runtime::traits::Zero; use sp_runtime::traits::Zero;
// see `struct AccountData` for details in substrate code
#[derive(Clone, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] #[derive(Clone, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
pub struct AccountData<Balance> { pub struct AccountData<Balance> {
/// A random identifier that can not be chosen by the user
// this intends to be used as a robust identification system
pub(super) random_id: Option<H256>, pub(super) random_id: Option<H256>,
// see Substrate AccountData
pub(super) free: Balance, pub(super) free: Balance,
// see Substrate AccountData
pub(super) reserved: Balance, pub(super) reserved: Balance,
// see Substrate AccountData
fee_frozen: Balance, fee_frozen: Balance,
} }
......
# Duniter test parameters
This pallet allows ĞDev runtime to tweak parameter values instead of having it runtime constants.
\ No newline at end of file
# Duniter Web of Trust pallet
Duniter WoT is at the core of its identity system and is a big improvement compared to PGP WoT. It is a dynamic directed graph whose nodes are [identities](../identity/) and edges [certifications](../certification/).
There are two instances:
- the main WoT, for every human
- the smith sub-WoT, for authorities
It has both static and dynamic rules, controlling the condition to join and remain [member](../membership/).
- static rules
- minimum number of received certifications (min indegree)
- maximum number of emited certifications (max outdegree)
- distance criterion (see distance pallet)
- dynamic rules
- time interval between two certifications
- certification duration (see certification pallet)
- membership renewal (see membership pallet)
This pallet's main role is to check the Web of Trust rules.
\ No newline at end of file
...@@ -162,6 +162,7 @@ where ...@@ -162,6 +162,7 @@ where
// TODO replace this code by the commented one for distance feature // TODO replace this code by the commented one for distance feature
/*let idty_cert_meta = pallet_certification::Pallet::<T, I>::idty_cert_meta(idty_index); /*let idty_cert_meta = pallet_certification::Pallet::<T, I>::idty_cert_meta(idty_index);
idty_cert_meta.received_count >= T::MinCertForMembership::get() as u32*/ idty_cert_meta.received_count >= T::MinCertForMembership::get() as u32*/
// in the main wot, automatically claim membership when the identity is validated
pallet_membership::Pallet::<T, I>::claim_membership( pallet_membership::Pallet::<T, I>::claim_membership(
RawOrigin::Root.into(), RawOrigin::Root.into(),
Some(idty_index), Some(idty_index),
...@@ -271,9 +272,9 @@ where ...@@ -271,9 +272,9 @@ where
} }
} }
// Membership revocation cases: // Membership revocation cases:
// - Triggered by identity removal: the identity underlying will by removed by the // - Triggered by main identity removal: the underlying identity will be removed by the
// caller. // caller.
// - Triggered by the membership pallet: it's ondly possible for the sub-wot, so we // - Triggered by the membership pallet: it's only possible for a sub-wot, so we
// should not remove the underlying identity // should not remove the underlying identity
// So, in any case, we must do nothing // So, in any case, we must do nothing
sp_membership::Event::<IdtyIndex, MetaData>::MembershipRevoked(_) => {} sp_membership::Event::<IdtyIndex, MetaData>::MembershipRevoked(_) => {}
......
# Duniter identity pallet
Duniter has a builtin identity system that does not work with external registrar compared to [parity identity pallet](https://github.com/paritytech/substrate/tree/master/frame/identity).
## Duniter identity
A Duniter identity contains:
- its **owner key** (that can change)
- an optional **old owner key** with the date of the key change
- a **status** that can be
- created (by an existing identity)
- confirmed (by owner, comes with a name)
- validated (that has become member in the allowed timeframe)
It also contains:
- the block number at which it can emit its **next certification**
- the block number at which it can be **removed from storage**
It also contains attached data defined by the runtime that can be for example
- the number of the first UD it is eligible to
### Name
Each identity is declared with a name emited on confirmation event. Duniter keeps a list of identity names hash to ensure unicity.
### Owner key
The idea of the owner key is to allow the user to keep a fixed identity while changing the keys for security reasons. For example when a device with the keys might have been compromised. There is a limit to the frequency of owner key change and the old owner key can still revoke the identity for a given period.
### Status / removable date
The status is a temporary value allowing to prune identities before they become member. When an identity is not valiated (not member of the WoT for instance), it can be removed when the date is reached. The remove date of a validated identity is block zero.
### Next certification
The next certification is a rate limit to the emission of certification (and then identity creation).
### Revokation
Revoking an identity basically means deleting it.
\ No newline at end of file
...@@ -169,6 +169,7 @@ pub mod pallet { ...@@ -169,6 +169,7 @@ pub mod pallet {
// STORAGE // // STORAGE //
/// maps identity index to identity value
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn identity)] #[pallet::getter(fn identity)]
pub type Identities<T: Config> = CountedStorageMap< pub type Identities<T: Config> = CountedStorageMap<
...@@ -179,19 +180,22 @@ pub mod pallet { ...@@ -179,19 +180,22 @@ pub mod pallet {
OptionQuery, OptionQuery,
>; >;
/// maps account id to identity index
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn identity_index_of)] #[pallet::getter(fn identity_index_of)]
pub type IdentityIndexOf<T: Config> = pub type IdentityIndexOf<T: Config> =
StorageMap<_, Blake2_128, T::AccountId, T::IdtyIndex, OptionQuery>; StorageMap<_, Blake2_128, T::AccountId, T::IdtyIndex, OptionQuery>;
/// maps identity name to null type (simply a set)
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn identity_by_did)] #[pallet::getter(fn identity_by_did)]
pub type IdentitiesNames<T: Config> = StorageMap<_, Blake2_128, IdtyName, (), OptionQuery>; pub type IdentitiesNames<T: Config> = StorageMap<_, Blake2_128, IdtyName, (), OptionQuery>;
/// counter of the identity index to give to the next identity
#[pallet::storage] #[pallet::storage]
pub(super) type NextIdtyIndex<T: Config> = StorageValue<_, T::IdtyIndex, ValueQuery>; pub(super) type NextIdtyIndex<T: Config> = StorageValue<_, T::IdtyIndex, ValueQuery>;
/// Identities by removed block /// maps block number to the list of identities set to be removed at this bloc
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn removable_on)] #[pallet::getter(fn removable_on)]
pub type IdentitiesRemovableOn<T: Config> = pub type IdentitiesRemovableOn<T: Config> =
...@@ -312,6 +316,7 @@ pub mod pallet { ...@@ -312,6 +316,7 @@ pub mod pallet {
T::OnIdtyChange::on_idty_change(idty_index, &IdtyEvent::Created { creator }); T::OnIdtyChange::on_idty_change(idty_index, &IdtyEvent::Created { creator });
Ok(().into()) Ok(().into())
} }
/// Confirm the creation of an identity and give it a name /// Confirm the creation of an identity and give it a name
/// ///
/// - `idty_name`: the name uniquely associated to this identity. Must match the validation rules defined by the runtime. /// - `idty_name`: the name uniquely associated to this identity. Must match the validation rules defined by the runtime.
...@@ -355,7 +360,9 @@ pub mod pallet { ...@@ -355,7 +360,9 @@ pub mod pallet {
T::OnIdtyChange::on_idty_change(idty_index, &IdtyEvent::Confirmed); T::OnIdtyChange::on_idty_change(idty_index, &IdtyEvent::Confirmed);
Ok(().into()) Ok(().into())
} }
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
/// validate the owned identity (must meet the main wot requirements)
pub fn validate_identity( pub fn validate_identity(
origin: OriginFor<T>, origin: OriginFor<T>,
idty_index: T::IdtyIndex, idty_index: T::IdtyIndex,
...@@ -517,6 +524,7 @@ pub mod pallet { ...@@ -517,6 +524,7 @@ pub mod pallet {
} }
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
/// remove an identity from storage
pub fn remove_identity( pub fn remove_identity(
origin: OriginFor<T>, origin: OriginFor<T>,
idty_index: T::IdtyIndex, idty_index: T::IdtyIndex,
...@@ -533,6 +541,7 @@ pub mod pallet { ...@@ -533,6 +541,7 @@ pub mod pallet {
} }
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
/// remove identity names from storage
pub fn prune_item_identities_names( pub fn prune_item_identities_names(
origin: OriginFor<T>, origin: OriginFor<T>,
names: Vec<IdtyName>, names: Vec<IdtyName>,
...@@ -547,6 +556,7 @@ pub mod pallet { ...@@ -547,6 +556,7 @@ pub mod pallet {
} }
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
/// change sufficient ref count for given key
pub fn fix_sufficients( pub fn fix_sufficients(
origin: OriginFor<T>, origin: OriginFor<T>,
owner_key: T::AccountId, owner_key: T::AccountId,
...@@ -625,6 +635,7 @@ pub mod pallet { ...@@ -625,6 +635,7 @@ pub mod pallet {
// INTERNAL FUNCTIONS // // INTERNAL FUNCTIONS //
impl<T: Config> Pallet<T> { impl<T: Config> Pallet<T> {
/// perform identity removal
pub(super) fn do_remove_identity(idty_index: T::IdtyIndex) -> Weight { pub(super) fn do_remove_identity(idty_index: T::IdtyIndex) -> Weight {
if let Some(idty_val) = Identities::<T>::get(idty_index) { if let Some(idty_val) = Identities::<T>::get(idty_index) {
let _ = T::RemoveIdentityConsumers::remove_idty_consumers(idty_index); let _ = T::RemoveIdentityConsumers::remove_idty_consumers(idty_index);
...@@ -645,6 +656,7 @@ pub mod pallet { ...@@ -645,6 +656,7 @@ pub mod pallet {
} }
Weight::zero() Weight::zero()
} }
/// incremental counter for identity index
fn get_next_idty_index() -> T::IdtyIndex { fn get_next_idty_index() -> T::IdtyIndex {
if let Ok(next_index) = <NextIdtyIndex<T>>::try_get() { if let Ok(next_index) = <NextIdtyIndex<T>>::try_get() {
<NextIdtyIndex<T>>::put(next_index.saturating_add(T::IdtyIndex::one())); <NextIdtyIndex<T>>::put(next_index.saturating_add(T::IdtyIndex::one()));
...@@ -654,6 +666,7 @@ pub mod pallet { ...@@ -654,6 +666,7 @@ pub mod pallet {
T::IdtyIndex::one() T::IdtyIndex::one()
} }
} }
/// remove identities planned for removal at the given block if their status did not change
fn prune_identities(block_number: T::BlockNumber) -> Weight { fn prune_identities(block_number: T::BlockNumber) -> Weight {
let mut total_weight = Weight::zero(); let mut total_weight = Weight::zero();
...@@ -670,16 +683,21 @@ pub mod pallet { ...@@ -670,16 +683,21 @@ pub mod pallet {
} }
} }
// implement getting owner key of identity index
impl<T: Config> sp_runtime::traits::Convert<T::IdtyIndex, Option<T::AccountId>> for Pallet<T> { impl<T: Config> sp_runtime::traits::Convert<T::IdtyIndex, Option<T::AccountId>> for Pallet<T> {
fn convert(idty_index: T::IdtyIndex) -> Option<T::AccountId> { fn convert(idty_index: T::IdtyIndex) -> Option<T::AccountId> {
Identities::<T>::get(idty_index).map(|idty_val| idty_val.owner_key) Identities::<T>::get(idty_index).map(|idty_val| idty_val.owner_key)
} }
} }
// implement StoredMap trait for this pallet
impl<T> frame_support::traits::StoredMap<T::AccountId, T::IdtyData> for Pallet<T> impl<T> frame_support::traits::StoredMap<T::AccountId, T::IdtyData> for Pallet<T>
where where
T: Config, T: Config,
{ {
/// get identity data for an account id
fn get(key: &T::AccountId) -> T::IdtyData { fn get(key: &T::AccountId) -> T::IdtyData {
if let Some(idty_index) = Self::identity_index_of(key) { if let Some(idty_index) = Self::identity_index_of(key) {
if let Some(idty_val) = Identities::<T>::get(idty_index) { if let Some(idty_val) = Identities::<T>::get(idty_index) {
...@@ -691,6 +709,7 @@ where ...@@ -691,6 +709,7 @@ where
Default::default() Default::default()
} }
} }
/// mutate an account fiven a function of its data
fn try_mutate_exists<R, E: From<sp_runtime::DispatchError>>( fn try_mutate_exists<R, E: From<sp_runtime::DispatchError>>(
key: &T::AccountId, key: &T::AccountId,
f: impl FnOnce(&mut Option<T::IdtyData>) -> Result<R, E>, f: impl FnOnce(&mut Option<T::IdtyData>) -> Result<R, E>,
......
...@@ -23,17 +23,25 @@ use scale_info::TypeInfo; ...@@ -23,17 +23,25 @@ use scale_info::TypeInfo;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sp_std::vec::Vec; use sp_std::vec::Vec;
/// events related to identity
pub enum IdtyEvent<T: crate::Config> { pub enum IdtyEvent<T: crate::Config> {
/// creation of a new identity by an other
Created { creator: T::IdtyIndex }, Created { creator: T::IdtyIndex },
/// confirmation of an identity (with a given name)
Confirmed, Confirmed,
/// validation of an identity
Validated, Validated,
/// changing the owner key of the identity
ChangedOwnerKey { new_owner_key: T::AccountId }, ChangedOwnerKey { new_owner_key: T::AccountId },
/// removing an identity
Removed { status: IdtyStatus }, Removed { status: IdtyStatus },
} }
/// name of the identity, ascii encoded
#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] #[derive(Encode, Decode, Default, Clone, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)]
pub struct IdtyName(pub Vec<u8>); pub struct IdtyName(pub Vec<u8>);
/// implement scale string typeinfo for encoding
impl scale_info::TypeInfo for IdtyName { impl scale_info::TypeInfo for IdtyName {
type Identity = str; type Identity = str;
...@@ -65,6 +73,8 @@ impl<'de> serde::Deserialize<'de> for IdtyName { ...@@ -65,6 +73,8 @@ impl<'de> serde::Deserialize<'de> for IdtyName {
} }
} }
/// status of the identity
/// used for temporary period before validation
#[cfg_attr(feature = "std", derive(Deserialize, Serialize))] #[cfg_attr(feature = "std", derive(Deserialize, Serialize))]
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub enum IdtyStatus { pub enum IdtyStatus {
...@@ -78,28 +88,44 @@ impl Default for IdtyStatus { ...@@ -78,28 +88,44 @@ impl Default for IdtyStatus {
} }
} }
/// identity value (as in key/value)
#[cfg_attr(feature = "std", derive(Debug, Deserialize, Serialize))] #[cfg_attr(feature = "std", derive(Debug, Deserialize, Serialize))]
#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] #[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
pub struct IdtyValue<BlockNumber, AccountId, IdtyData> { pub struct IdtyValue<BlockNumber, AccountId, IdtyData> {
/// data shared between pallets defined by runtime
/// only contains first_eligible_ud in our case
pub data: IdtyData, pub data: IdtyData,
/// block before which creating a new identity is not allowed
pub next_creatable_identity_on: BlockNumber, pub next_creatable_identity_on: BlockNumber,
/// previous owner key of this identity (optional)
pub old_owner_key: Option<(AccountId, BlockNumber)>, pub old_owner_key: Option<(AccountId, BlockNumber)>,
/// current owner key of this identity
pub owner_key: AccountId, pub owner_key: AccountId,
/// block before which this identity can not be removed
/// used only for temporary period before validation
/// equals 0 for a validated identity
pub removable_on: BlockNumber, pub removable_on: BlockNumber,
/// current status of the identity (until validation)
pub status: IdtyStatus, pub status: IdtyStatus,
} }
/// payload to define a new owner key
#[derive(Clone, Copy, Encode, RuntimeDebug)] #[derive(Clone, Copy, Encode, RuntimeDebug)]
pub struct NewOwnerKeyPayload<'a, AccountId, IdtyIndex, Hash> { pub struct NewOwnerKeyPayload<'a, AccountId, IdtyIndex, Hash> {
/// hash of the genesis block
// Avoid replay attack between networks // Avoid replay attack between networks
pub genesis_hash: &'a Hash, pub genesis_hash: &'a Hash,
/// identity index
pub idty_index: IdtyIndex, pub idty_index: IdtyIndex,
/// old owner key of the identity
pub old_owner_key: &'a AccountId, pub old_owner_key: &'a AccountId,
} }
#[derive(Clone, Copy, Encode, Decode, PartialEq, Eq, TypeInfo, RuntimeDebug)] #[derive(Clone, Copy, Encode, Decode, PartialEq, Eq, TypeInfo, RuntimeDebug)]
pub struct RevocationPayload<IdtyIndex, Hash> { pub struct RevocationPayload<IdtyIndex, Hash> {
/// hash of the genesis block
// Avoid replay attack between networks // Avoid replay attack between networks
pub genesis_hash: Hash, pub genesis_hash: Hash,
/// identity index
pub idty_index: IdtyIndex, pub idty_index: IdtyIndex,
} }
# Duniter membership pallet
Duniter membership is related to duniter Web of Trust and more specific than [parity membership pallet](https://github.com/paritytech/substrate/tree/master/frame/membership). It is used only internally by the identity, WoT, and distance pallets. In particular, it is adding the concept of "pending membership" which is an intermediate state where the identity is waiting to become member.
\ No newline at end of file
...@@ -108,21 +108,26 @@ pub mod pallet { ...@@ -108,21 +108,26 @@ pub mod pallet {
// STORAGE // // STORAGE //
/// maps identity id to membership data
// (expiration block for instance)
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn membership)] #[pallet::getter(fn membership)]
pub type Membership<T: Config<I>, I: 'static = ()> = pub type Membership<T: Config<I>, I: 'static = ()> =
CountedStorageMap<_, Twox64Concat, T::IdtyId, MembershipData<T::BlockNumber>, OptionQuery>; CountedStorageMap<_, Twox64Concat, T::IdtyId, MembershipData<T::BlockNumber>, OptionQuery>;
/// maps block number to the list of identity id set to expire at this block
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn memberships_expire_on)] #[pallet::getter(fn memberships_expire_on)]
pub type MembershipsExpireOn<T: Config<I>, I: 'static = ()> = pub type MembershipsExpireOn<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, T::BlockNumber, Vec<T::IdtyId>, ValueQuery>; StorageMap<_, Twox64Concat, T::BlockNumber, Vec<T::IdtyId>, ValueQuery>;
/// maps identity id to pending membership metadata
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn pending_membership)] #[pallet::getter(fn pending_membership)]
pub type PendingMembership<T: Config<I>, I: 'static = ()> = pub type PendingMembership<T: Config<I>, I: 'static = ()> =
StorageMap<_, Twox64Concat, T::IdtyId, T::MetaData, OptionQuery>; StorageMap<_, Twox64Concat, T::IdtyId, T::MetaData, OptionQuery>;
/// maps block number to the list of memberships set to expire at this block
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn pending_memberships_expire_on)] #[pallet::getter(fn pending_memberships_expire_on)]
pub type PendingMembershipsExpireOn<T: Config<I>, I: 'static = ()> = pub type PendingMembershipsExpireOn<T: Config<I>, I: 'static = ()> =
...@@ -133,19 +138,19 @@ pub mod pallet { ...@@ -133,19 +138,19 @@ pub mod pallet {
#[pallet::event] #[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)] #[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config<I>, I: 'static = ()> { pub enum Event<T: Config<I>, I: 'static = ()> {
/// A membership has acquired /// A membership was acquired
/// [idty_id] /// [idty_id]
MembershipAcquired(T::IdtyId), MembershipAcquired(T::IdtyId),
/// A membership has expired /// A membership expired
/// [idty_id] /// [idty_id]
MembershipExpired(T::IdtyId), MembershipExpired(T::IdtyId),
/// A membership has renewed /// A membership was renewed
/// [idty_id] /// [idty_id]
MembershipRenewed(T::IdtyId), MembershipRenewed(T::IdtyId),
/// An identity requested membership /// An membership was requested
/// [idty_id] /// [idty_id]
MembershipRequested(T::IdtyId), MembershipRequested(T::IdtyId),
/// A membership has revoked /// A membership was revoked
/// [idty_id] /// [idty_id]
MembershipRevoked(T::IdtyId), MembershipRevoked(T::IdtyId),
/// A pending membership request has expired /// A pending membership request has expired
...@@ -191,6 +196,7 @@ pub mod pallet { ...@@ -191,6 +196,7 @@ pub mod pallet {
#[pallet::call] #[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> { impl<T: Config<I>, I: 'static> Pallet<T, I> {
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
/// request membership without checks
pub fn force_request_membership( pub fn force_request_membership(
origin: OriginFor<T>, origin: OriginFor<T>,
idty_id: T::IdtyId, idty_id: T::IdtyId,
...@@ -202,6 +208,8 @@ pub mod pallet { ...@@ -202,6 +208,8 @@ pub mod pallet {
} }
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
/// submit a membership request (must have a declared identity)
/// (only available for sub wot, automatic for main wot)
pub fn request_membership( pub fn request_membership(
origin: OriginFor<T>, origin: OriginFor<T>,
metadata: T::MetaData, metadata: T::MetaData,
...@@ -218,6 +226,8 @@ pub mod pallet { ...@@ -218,6 +226,8 @@ pub mod pallet {
} }
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
/// claim that the previously requested membership fullfills the requirements
/// (only available for sub wot, automatic for main wot)
pub fn claim_membership( pub fn claim_membership(
origin: OriginFor<T>, origin: OriginFor<T>,
maybe_idty_id: Option<T::IdtyId>, maybe_idty_id: Option<T::IdtyId>,
...@@ -244,6 +254,7 @@ pub mod pallet { ...@@ -244,6 +254,7 @@ pub mod pallet {
} }
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
/// extend the validity period of an active membership
pub fn renew_membership( pub fn renew_membership(
origin: OriginFor<T>, origin: OriginFor<T>,
maybe_idty_id: Option<T::IdtyId>, maybe_idty_id: Option<T::IdtyId>,
...@@ -264,6 +275,8 @@ pub mod pallet { ...@@ -264,6 +275,8 @@ pub mod pallet {
} }
#[pallet::weight(1_000_000_000)] #[pallet::weight(1_000_000_000)]
/// revoke an active membership
/// (only available for sub wot, automatic for main wot)
pub fn revoke_membership( pub fn revoke_membership(
origin: OriginFor<T>, origin: OriginFor<T>,
maybe_idty_id: Option<T::IdtyId>, maybe_idty_id: Option<T::IdtyId>,
...@@ -284,12 +297,14 @@ pub mod pallet { ...@@ -284,12 +297,14 @@ pub mod pallet {
// INTERNAL FUNCTIONS // // INTERNAL FUNCTIONS //
impl<T: Config<I>, I: 'static> Pallet<T, I> { impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// perform the membership renewal and emit events
pub(super) fn do_renew_membership(idty_id: T::IdtyId) -> Weight { pub(super) fn do_renew_membership(idty_id: T::IdtyId) -> Weight {
let total_weight = Self::do_renew_membership_inner(idty_id); let total_weight = Self::do_renew_membership_inner(idty_id);
Self::deposit_event(Event::MembershipRenewed(idty_id)); Self::deposit_event(Event::MembershipRenewed(idty_id));
T::OnEvent::on_event(&sp_membership::Event::MembershipRenewed(idty_id)); T::OnEvent::on_event(&sp_membership::Event::MembershipRenewed(idty_id));
total_weight total_weight
} }
/// perform the membership renewal
fn do_renew_membership_inner(idty_id: T::IdtyId) -> Weight { fn do_renew_membership_inner(idty_id: T::IdtyId) -> Weight {
let block_number = frame_system::pallet::Pallet::<T>::block_number(); let block_number = frame_system::pallet::Pallet::<T>::block_number();
let expire_on = block_number + T::MembershipPeriod::get(); let expire_on = block_number + T::MembershipPeriod::get();
...@@ -298,6 +313,7 @@ pub mod pallet { ...@@ -298,6 +313,7 @@ pub mod pallet {
MembershipsExpireOn::<T, I>::append(expire_on, idty_id); MembershipsExpireOn::<T, I>::append(expire_on, idty_id);
Weight::zero() Weight::zero()
} }
/// perform the membership request
fn do_request_membership( fn do_request_membership(
idty_id: T::IdtyId, idty_id: T::IdtyId,
metadata: T::MetaData, metadata: T::MetaData,
...@@ -319,6 +335,7 @@ pub mod pallet { ...@@ -319,6 +335,7 @@ pub mod pallet {
Ok(().into()) Ok(().into())
} }
/// check the origin and get identity id if valid
fn ensure_origin_and_get_idty_id( fn ensure_origin_and_get_idty_id(
origin: OriginFor<T>, origin: OriginFor<T>,
maybe_idty_id: Option<T::IdtyId>, maybe_idty_id: Option<T::IdtyId>,
...@@ -332,6 +349,7 @@ pub mod pallet { ...@@ -332,6 +349,7 @@ pub mod pallet {
_ => Err(BadOrigin.into()), _ => Err(BadOrigin.into()),
} }
} }
/// perform the membership expiration if it has not been renewed meanwhile
fn expire_memberships(block_number: T::BlockNumber) -> Weight { fn expire_memberships(block_number: T::BlockNumber) -> Weight {
let mut total_weight: Weight = Weight::zero(); let mut total_weight: Weight = Weight::zero();
...@@ -348,6 +366,7 @@ pub mod pallet { ...@@ -348,6 +366,7 @@ pub mod pallet {
total_weight total_weight
} }
/// perform the expiration of pending membership planned at given block
fn expire_pending_memberships(block_number: T::BlockNumber) -> Weight { fn expire_pending_memberships(block_number: T::BlockNumber) -> Weight {
let mut total_weight: Weight = Weight::zero(); let mut total_weight: Weight = Weight::zero();
...@@ -363,21 +382,27 @@ pub mod pallet { ...@@ -363,21 +382,27 @@ pub mod pallet {
total_weight total_weight
} }
/// check if identity is member
pub(super) fn is_member_inner(idty_id: &T::IdtyId) -> bool { pub(super) fn is_member_inner(idty_id: &T::IdtyId) -> bool {
Membership::<T, I>::contains_key(idty_id) Membership::<T, I>::contains_key(idty_id)
} }
/// insert membership in storage
fn insert_membership(idty_id: T::IdtyId, membership_data: MembershipData<T::BlockNumber>) { fn insert_membership(idty_id: T::IdtyId, membership_data: MembershipData<T::BlockNumber>) {
Membership::<T, I>::insert(idty_id, membership_data); Membership::<T, I>::insert(idty_id, membership_data);
} }
/// retreive membership from storage
fn get_membership(idty_id: &T::IdtyId) -> Option<MembershipData<T::BlockNumber>> { fn get_membership(idty_id: &T::IdtyId) -> Option<MembershipData<T::BlockNumber>> {
Membership::<T, I>::try_get(idty_id).ok() Membership::<T, I>::try_get(idty_id).ok()
} }
/// remove membership from storage
fn remove_membership(idty_id: &T::IdtyId) -> bool { fn remove_membership(idty_id: &T::IdtyId) -> bool {
Membership::<T, I>::take(idty_id).is_some() Membership::<T, I>::take(idty_id).is_some()
} }
} }
} }
// implement traits
impl<T: Config<I>, I: 'static> IsInPendingMemberships<T::IdtyId> for Pallet<T, I> { impl<T: Config<I>, I: 'static> IsInPendingMemberships<T::IdtyId> for Pallet<T, I> {
fn is_in_pending_memberships(idty_id: T::IdtyId) -> bool { fn is_in_pending_memberships(idty_id: T::IdtyId) -> bool {
PendingMembership::<T, I>::contains_key(idty_id) PendingMembership::<T, I>::contains_key(idty_id)
......
# Duniter oneshot account pallet
Duniter provides light accounts without `AccountInfo` (nonce, consumers, providers, sufficients, free, reserved, misc_frozen, fee_frozen) that can only be consumed once. This should reduce transaction weight and then fees. The use case is anonymous accounts or physical supports.
\ No newline at end of file
# Duniter provide randomness pallet
TODO
\ No newline at end of file
# Duniter universal dividend pallet
One of the main features of Duniter is the Universal Dividend based on the Relative Theory of Money. It is both a daily monetary creation and a measure unit.
This pallet provides functions to create UDs and transfer an amount of currency counted in UD. It should be noted that the UD is not actually created every day on every account which would be very resource consuming but must be claimed by the member in a given extrinsic.
\ No newline at end of file
...@@ -254,7 +254,9 @@ pub mod pallet { ...@@ -254,7 +254,9 @@ pub mod pallet {
// INTERNAL FUNCTIONS // // INTERNAL FUNCTIONS //
impl<T: Config> Pallet<T> { impl<T: Config> Pallet<T> {
/// create universal dividend
fn create_ud(members_count: BalanceOf<T>) { fn create_ud(members_count: BalanceOf<T>) {
// get current value of UD and monetary mass
let ud_amount = <CurrentUd<T>>::get(); let ud_amount = <CurrentUd<T>>::get();
let monetary_mass = <MonetaryMass<T>>::get(); let monetary_mass = <MonetaryMass<T>>::get();
...@@ -263,9 +265,14 @@ pub mod pallet { ...@@ -263,9 +265,14 @@ pub mod pallet {
core::mem::replace(next_ud_index, next_ud_index.saturating_add(1)) core::mem::replace(next_ud_index, next_ud_index.saturating_add(1))
}); });
// compute the new monetary mass
let new_monetary_mass = let new_monetary_mass =
monetary_mass.saturating_add(ud_amount.saturating_mul(members_count)); monetary_mass.saturating_add(ud_amount.saturating_mul(members_count));
// update the storage value of the monetary mass
MonetaryMass::<T>::put(new_monetary_mass); MonetaryMass::<T>::put(new_monetary_mass);
// emit an event to inform blockchain users that the holy UNIVERSAL DIVIDEND was created
Self::deposit_event(Event::NewUdCreated { Self::deposit_event(Event::NewUdCreated {
amount: ud_amount, amount: ud_amount,
index: ud_index, index: ud_index,
...@@ -273,6 +280,8 @@ pub mod pallet { ...@@ -273,6 +280,8 @@ pub mod pallet {
monetary_mass: new_monetary_mass, monetary_mass: new_monetary_mass,
}); });
} }
/// claim all due universal dividend at a time
fn do_claim_uds(who: &T::AccountId) -> DispatchResultWithPostInfo { fn do_claim_uds(who: &T::AccountId) -> DispatchResultWithPostInfo {
T::MembersStorage::try_mutate_exists(who, |maybe_first_eligible_ud| { T::MembersStorage::try_mutate_exists(who, |maybe_first_eligible_ud| {
if let Some(FirstEligibleUd(Some(ref mut first_ud_index))) = maybe_first_eligible_ud if let Some(FirstEligibleUd(Some(ref mut first_ud_index))) = maybe_first_eligible_ud
...@@ -304,6 +313,8 @@ pub mod pallet { ...@@ -304,6 +313,8 @@ pub mod pallet {
} }
}) })
} }
/// like balance.transfer, but give an amount in UD
fn do_transfer_ud( fn do_transfer_ud(
origin: OriginFor<T>, origin: OriginFor<T>,
dest: <T::Lookup as StaticLookup>::Source, dest: <T::Lookup as StaticLookup>::Source,
...@@ -321,11 +332,14 @@ pub mod pallet { ...@@ -321,11 +332,14 @@ pub mod pallet {
)?; )?;
Ok(().into()) Ok(().into())
} }
/// reevaluate the value of the universal dividend
fn reeval_ud(members_count: BalanceOf<T>) { fn reeval_ud(members_count: BalanceOf<T>) {
// get current value and monetary mass
let ud_amount = <CurrentUd<T>>::get(); let ud_amount = <CurrentUd<T>>::get();
let monetary_mass = <MonetaryMass<T>>::get(); let monetary_mass = <MonetaryMass<T>>::get();
// compute new value
let new_ud_amount = Self::reeval_ud_formula( let new_ud_amount = Self::reeval_ud_formula(
ud_amount, ud_amount,
T::SquareMoneyGrowthRate::get(), T::SquareMoneyGrowthRate::get(),
...@@ -336,6 +350,7 @@ pub mod pallet { ...@@ -336,6 +350,7 @@ pub mod pallet {
), ),
); );
// update the storage value and the history of past reevals
CurrentUd::<T>::put(new_ud_amount); CurrentUd::<T>::put(new_ud_amount);
PastReevals::<T>::mutate(|past_reevals| { PastReevals::<T>::mutate(|past_reevals| {
if past_reevals.len() == T::MaxPastReeval::get() as usize { if past_reevals.len() == T::MaxPastReeval::get() as usize {
...@@ -352,6 +367,8 @@ pub mod pallet { ...@@ -352,6 +367,8 @@ pub mod pallet {
members_count, members_count,
}); });
} }
/// formula for Universal Dividend reevaluation
fn reeval_ud_formula( fn reeval_ud_formula(
ud_t: BalanceOf<T>, ud_t: BalanceOf<T>,
c_square: Perbill, c_square: Perbill,
...@@ -406,6 +423,8 @@ pub mod pallet { ...@@ -406,6 +423,8 @@ pub mod pallet {
pub fn init_first_eligible_ud() -> FirstEligibleUd { pub fn init_first_eligible_ud() -> FirstEligibleUd {
CurrentUdIndex::<T>::get().into() CurrentUdIndex::<T>::get().into()
} }
/// function to call when removing a member
/// auto-claims UDs
pub fn on_removed_member(first_ud_index: UdIndex, who: &T::AccountId) -> Weight { pub fn on_removed_member(first_ud_index: UdIndex, who: &T::AccountId) -> Weight {
let current_ud_index = CurrentUdIndex::<T>::get(); let current_ud_index = CurrentUdIndex::<T>::get();
if first_ud_index < current_ud_index { if first_ud_index < current_ud_index {
......
# Duniter upgrade origin pallet
TODO
\ No newline at end of file
# Primitives
TODO
\ No newline at end of file
# Resources
Files used for different purpose like tests.
\ No newline at end of file
No preview for this file type