Skip to content
Snippets Groups Projects
Commit b793b455 authored by Hugo Trentesaux's avatar Hugo Trentesaux
Browse files

implement quotas and refund transaction fees (!183)

* implement quotas

implement weights "à l'arrache"

benchmarks duniter-account "à l'arrache"

implement benchmark logic (not proper benchmarks)

fix live tests :man_facepalming:

and clippy :man_facepalming::man_facepalming:

replace quotas by quota everywhere

comment unused sections of template

remove quota treasury dependency

give treasury address as argument

typo

review tuxmain

doc readme

rename error DistanceKO to DistanceNotOK

merge new owner key and revocation signature

merge signature error types

rename NewOwnerKeyPayload

fix comment

make eligibility more explicit

implement quotas

implement weights "à l'arrache"

benchmarks duniter-account "à l'arrache"

implement benchmark logic (not proper benchmarks)

fix live tests :man_facepalming:

and clippy :man_facepalming::man_facepalming:

replace quotas by quota everywhere

comment unused sections of template

remove quota treasury dependency

give treasury address as argument

typo

review tuxmain

doc readme

rename error DistanceKO to DistanceNotOK

merge new owner key and revocation signature

merge signature error types

rename NewOwnerKeyPayload

fix comment

make eligibility more explicit

update metadata

fix

fix fee multiplier update

prevent network discovery + connecting other nodes
parent 9e3659a2
Branches
Tags
1 merge request!183implement quotas and refund transaction fees
Pipeline #34208 waiting for manual action
Showing
with 630 additions and 168 deletions
......@@ -1048,6 +1048,7 @@ dependencies = [
"pallet-preimage",
"pallet-provide-randomness",
"pallet-proxy",
"pallet-quota",
"pallet-scheduler",
"pallet-session",
"pallet-session-benchmarking",
......@@ -3004,6 +3005,7 @@ dependencies = [
"pallet-preimage",
"pallet-provide-randomness",
"pallet-proxy",
"pallet-quota",
"pallet-scheduler",
"pallet-session",
"pallet-sudo",
......@@ -3072,6 +3074,7 @@ dependencies = [
"pallet-preimage",
"pallet-provide-randomness",
"pallet-proxy",
"pallet-quota",
"pallet-scheduler",
"pallet-session",
"pallet-session-benchmarking",
......@@ -3375,6 +3378,7 @@ dependencies = [
"pallet-preimage",
"pallet-provide-randomness",
"pallet-proxy",
"pallet-quota",
"pallet-scheduler",
"pallet-session",
"pallet-session-benchmarking",
......@@ -3396,6 +3400,7 @@ dependencies = [
"sp-block-builder",
"sp-consensus-babe",
"sp-core",
"sp-distance",
"sp-inherents",
"sp-io",
"sp-keyring",
......@@ -5791,7 +5796,10 @@ dependencies = [
"log",
"maplit",
"pallet-balances",
"pallet-identity",
"pallet-provide-randomness",
"pallet-quota",
"pallet-transaction-payment",
"pallet-treasury",
"parity-scale-codec",
"scale-info",
......@@ -6028,6 +6036,23 @@ dependencies = [
"sp-std 5.0.0",
]
[[package]]
name = "pallet-quota"
version = "3.0.0"
dependencies = [
"frame-benchmarking",
"frame-support",
"frame-system",
"pallet-balances",
"pallet-identity",
"parity-scale-codec",
"scale-info",
"sp-core",
"sp-io",
"sp-runtime",
"sp-std 5.0.0",
]
[[package]]
name = "pallet-scheduler"
version = "4.0.0-dev"
......
......@@ -145,6 +145,7 @@ members = [
'end2end-tests',
'live-tests',
'pallets/certification',
'pallets/quota',
'pallets/distance',
'pallets/duniter-test-parameters',
'pallets/duniter-test-parameters/macro',
......
This diff is collapsed.
......@@ -3,11 +3,11 @@ Feature: Identity creation
Scenario: alice invites a new member to join the web of trust
# 6 ĞD covers:
# - account creation fees (3 ĞD)
# - existential deposit (2 ĞD)
# - existential deposit (1 ĞD)
# - transaction fees (below 1 ĞD)
When alice sends 7 ĞD to dave
# Alice is treasury funder for 1 ĞD => 10-1-7 = 2
Then alice should have 2 ĞD
# Alice is treasury funder for 1 ĞD => 10-1-7 = 2 (minus fees)
Then alice should have 199 cĞD
When bob sends 750 cĞD to dave
When charlie sends 6 ĞD to eve
# alice last certification is counted from block zero
......
......@@ -2,22 +2,22 @@ Feature: Oneshot account
Scenario: Simple oneshot consumption
When alice sends 7 ĞD to oneshot dave
# Alice is treasury funder for 1 ĞD
Then alice should have 2 ĞD
# Alice is treasury funder for 1 ĞD and pays fees
Then alice should have 199 cĞD
Then dave should have oneshot 7 ĞD
When oneshot dave consumes into account bob
Then dave should have oneshot 0 ĞD
Then bob should have 1699 cĞD
Then bob should have 1698 cĞD
Then bob should have oneshot 0 ĞD
Scenario: Double oneshot consumption
When alice sends 7 ĞD to oneshot dave
# Alice is treasury funder for 1 ĞD
Then alice should have 2 ĞD
# Alice is treasury funder for 1 ĞD and pays fees
Then alice should have 199 cĞD
Then dave should have oneshot 7 ĞD
When oneshot dave consumes 4 ĞD into account bob and the rest into oneshot charlie
Then dave should have oneshot 0 ĞD
Then bob should have 14 ĞD
Then bob should have oneshot 0 ĞD
Then charlie should have 10 ĞD
Then charlie should have oneshot 299 cĞD
Then charlie should have oneshot 298 cĞD
......@@ -6,9 +6,12 @@ Feature: Balance transfer all
"""
Bob is a member, as such he is not allowed to empty his account completely,
if he tries to do so, the existence deposit (1 ĞD) must remain.
Bob is a member, transaction fees are refunded for him
101 = existential deposit (100) + fees refunded using quota (001)
"""
Then bob should have 1 ĞD
Then bob should have 101 cĞD
"""
10 ĞD (initial Bob balance) - 1 ĞD (Existential deposit) - 0.02 ĞD (transaction fees)
"""
Then dave should have 898 cĞD
# TODO check that the missing cent went to treasury
......@@ -137,6 +137,8 @@ pub async fn spawn_node(
"--tmp",
// Fix: End2End test may fail due to network discovery. This option disables automatic peer discovery.π
"--reserved-only",
// prevent local network discovery (even it does not connect due to above flag)
"--no-mdns",
],
&duniter_binary_path,
maybe_genesis_conf_file,
......
......@@ -38,7 +38,7 @@ type Index = u32;
// Define gdev types
type AccountInfo = gdev::runtime_types::frame_system::AccountInfo<
Index,
gdev::runtime_types::pallet_duniter_account::types::AccountData<Balance>,
gdev::runtime_types::pallet_duniter_account::types::AccountData<Balance, IdtyIndex>,
>;
type IdtyData = gdev::runtime_types::common_runtime::entities::IdtyData;
type IdtyIndex = u32;
......
......@@ -24,7 +24,7 @@ use common_runtime::*;
use gdev_runtime::{
opaque::SessionKeys, parameters, AccountConfig, AuthorityMembersConfig, BabeConfig,
BalancesConfig, CertConfig, GenesisConfig, IdentityConfig, MembershipConfig, ParametersConfig,
SessionConfig, SmithCertConfig, SmithMembershipConfig, SudoConfig, SystemConfig,
QuotaConfig, SessionConfig, SmithCertConfig, SmithMembershipConfig, SudoConfig, SystemConfig,
TechnicalCommitteeConfig, UniversalDividendConfig, WASM_BINARY,
};
use jsonrpsee::core::JsonValue;
......@@ -302,6 +302,13 @@ fn genesis_data_to_gdev_genesis_conf(
members: technical_committee_members,
..Default::default()
},
quota: QuotaConfig {
identities: identities
.iter()
.enumerate()
.map(|(i, _)| i as u32 + 1)
.collect(),
},
identity: IdentityConfig {
identities: identities
.into_iter()
......
......@@ -64,7 +64,7 @@ type MembershipData = sp_membership::MembershipData<u32>;
#[derive(Clone)]
pub struct GenesisData<Parameters: DeserializeOwned, SessionKeys: Decode> {
pub accounts: BTreeMap<AccountId, GenesisAccountData<u64>>,
pub accounts: BTreeMap<AccountId, GenesisAccountData<u64, u32>>,
pub treasury_balance: u64,
pub certs_by_receiver: BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
pub first_ud: Option<u64>,
......@@ -237,7 +237,7 @@ struct SmithWoT<SK: Decode> {
}
struct GenesisInfo<'a> {
accounts: &'a BTreeMap<AccountId32, GenesisAccountData<u64>>,
accounts: &'a BTreeMap<AccountId32, GenesisAccountData<u64, u32>>,
genesis_data_wallets_count: &'a usize,
inactive_identities: &'a HashMap<u32, String>,
identities: &'a Vec<GenesisIdentity>,
......@@ -888,13 +888,13 @@ fn v1_wallets_to_v2_accounts(
) -> (
bool,
u64,
BTreeMap<AccountId32, GenesisAccountData<u64>>,
BTreeMap<AccountId32, GenesisAccountData<u64, u32>>,
usize,
) {
// monetary mass for double check
let mut monetary_mass = 0u64;
// account inserted in genesis
let mut accounts: BTreeMap<AccountId, GenesisAccountData<u64>> = BTreeMap::new();
let mut accounts: BTreeMap<AccountId, GenesisAccountData<u64, u32>> = BTreeMap::new();
let mut invalid_wallets = 0;
let mut fatal = false;
for (pubkey, balance) in wallets {
......@@ -917,7 +917,7 @@ fn v1_wallets_to_v2_accounts(
GenesisAccountData {
random_id: H256(blake2_256(&(balance, &owner_key).encode())),
balance,
is_identity: false,
idty_id: None,
},
);
} else {
......@@ -1147,7 +1147,7 @@ fn make_authority_exist<SessionKeys: Encode, SKP: SessionKeysProvider<SessionKey
}
fn feed_identities(
accounts: &mut BTreeMap<AccountId32, GenesisAccountData<u64>>,
accounts: &mut BTreeMap<AccountId32, GenesisAccountData<u64, u32>>,
identity_index: &mut HashMap<u32, String>,
monetary_mass: &mut u64,
inactive_identities: &mut HashMap<u32, String>,
......@@ -1196,7 +1196,7 @@ fn feed_identities(
GenesisAccountData {
random_id: H256(blake2_256(&(identity.index, &identity.owner_key).encode())),
balance: identity.balance,
is_identity: true,
idty_id: Some(identity.index),
},
);
......@@ -1499,7 +1499,7 @@ where
&(i as u32 + idty_index_start, owner_key).encode(),
)),
balance: ud,
is_identity: true,
idty_id: Some(i as u32 + idty_index_start),
},
)
})
......
......@@ -32,6 +32,8 @@ try-runtime = ['frame-support/try-runtime']
[dependencies]
# local
pallet-quota = { path = "../quota", default-features = false }
pallet-identity = { path = "../identity", default-features = false }
pallet-provide-randomness = { path = "../provide-randomness", default-features = false }
# crates.io
......@@ -61,6 +63,11 @@ default-features = false
git = 'https://github.com/duniter/substrate'
branch = 'duniter-substrate-v0.9.42'
[dependencies.pallet-transaction-payment]
default-features = false
git = 'https://github.com/duniter/substrate'
branch = 'duniter-substrate-v0.9.42'
[dependencies.pallet-treasury]
default-features = false
git = 'https://github.com/duniter/substrate'
......
# Duniter account pallet
Duniter customizes the `AccountData` of the `Balances` Substrate pallet. In particular, it adds a field `RandomId`.
Duniter customizes the `AccountData` of the `Balances` Substrate pallet. In particular, it adds the fields `random_id` and `linked_idty`.
## RandomID
......@@ -15,3 +15,7 @@ DuniterAccount defines a creation fee that is preleved to the account one block
## Sufficient
DuniterAccount tweaks the substrate AccountInfo to allow identity accounts to exist without existential deposit. This allows to spare the creation fee.
## Linked identity
Duniter offers the possibility to link an account to an identity with the `linked_idty` field. It allows to request refund of transaction fees in `OnChargeTransaction`.
\ No newline at end of file
......@@ -18,7 +18,7 @@
use super::*;
use frame_benchmarking::{benchmarks, whitelisted_caller};
use frame_benchmarking::{account, benchmarks, whitelisted_caller};
use frame_support::sp_runtime::{traits::One, Saturating};
use frame_support::traits::{Currency, Get};
use pallet_provide_randomness::OnFilledRandomness;
......@@ -60,6 +60,10 @@ fn create_pending_accounts<T: Config>(
}
benchmarks! {
unlink_identity {
let account = account("Alice", 1, 1);
let origin = frame_system::RawOrigin::Signed(account);
}: _<T::RuntimeOrigin>(origin.into())
on_initialize_sufficient {
let i in 0 .. T::MaxNewAccountsPerBlock::get() => create_pending_accounts::<T>(i, false, true)?;
}: { Pallet::<T>::on_initialize(T::BlockNumber::one()); }
......
......@@ -14,6 +14,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
// Note: refund queue mechanism is inspired from frame contract
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "runtime-benchmarks")]
......@@ -27,16 +29,24 @@ pub use types::*;
pub use weights::WeightInfo;
use frame_support::pallet_prelude::*;
use frame_support::traits::{Currency, ExistenceRequirement, StorageVersion};
use frame_support::traits::{OnUnbalanced, StoredMap};
use frame_system::pallet_prelude::*;
use pallet_identity::IdtyEvent;
use pallet_provide_randomness::RequestId;
use pallet_quota::traits::RefundFee;
use pallet_transaction_payment::OnChargeTransaction;
use sp_core::H256;
use sp_runtime::traits::{Convert, Saturating};
use sp_runtime::traits::{Convert, DispatchInfoOf, PostDispatchInfoOf, Saturating};
use sp_std::fmt::Debug;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::traits::{Currency, ExistenceRequirement, StorageVersion};
pub type IdtyIdOf<T> = <T as pallet_identity::Config>::IdtyIndex;
pub type CurrencyOf<T> = pallet_balances::Pallet<T>;
pub type BalanceOf<T> =
<CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::Balance;
/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
......@@ -50,10 +60,12 @@ pub mod pallet {
#[pallet::config]
pub trait Config:
frame_system::Config<AccountData = AccountData<Self::Balance>>
frame_system::Config<AccountData = AccountData<Self::Balance, IdtyIdOf<Self>>>
+ pallet_balances::Config
+ pallet_provide_randomness::Config<Currency = pallet_balances::Pallet<Self>>
+ pallet_transaction_payment::Config
+ pallet_treasury::Config<Currency = pallet_balances::Pallet<Self>>
+ pallet_quota::Config
{
type AccountIdToSalt: Convert<Self::AccountId, [u8; 32]>;
#[pallet::constant]
......@@ -64,6 +76,10 @@ pub mod pallet {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Type representing the weight of this pallet
type WeightInfo: WeightInfo;
/// wrapped type
type InnerOnChargeTransaction: OnChargeTransaction<Self>;
/// type implementing refund behavior
type Refund: pallet_quota::traits::RefundFee<Self>;
}
// STORAGE //
......@@ -82,8 +98,10 @@ pub mod pallet {
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub accounts:
sp_std::collections::btree_map::BTreeMap<T::AccountId, GenesisAccountData<T::Balance>>,
pub accounts: sp_std::collections::btree_map::BTreeMap<
T::AccountId,
GenesisAccountData<T::Balance, IdtyIdOf<T>>,
>,
pub treasury_balance: T::Balance,
}
......@@ -107,7 +125,6 @@ pub mod pallet {
account.data.random_id = None;
account.data.free = self.treasury_balance;
account.providers = 1;
account.sufficients = 1; // the treasury will always be self-sufficient
},
);
......@@ -129,16 +146,19 @@ pub mod pallet {
GenesisAccountData {
random_id,
balance,
is_identity,
idty_id,
},
) in &self.accounts
{
// if the balance is below existential deposit, the account must be an identity
assert!(balance >= &T::ExistentialDeposit::get() || *is_identity);
assert!(balance >= &T::ExistentialDeposit::get() || idty_id.is_some());
// mutate account
frame_system::Account::<T>::mutate(account_id, |account| {
account.data.random_id = Some(*random_id);
account.data.free = *balance;
if idty_id.is_some() {
account.data.linked_idty = *idty_id;
}
if balance >= &T::ExistentialDeposit::get() {
// accounts above existential deposit self-provide
account.providers = 1;
......@@ -166,11 +186,64 @@ pub mod pallet {
/// Random id assigned
/// [account_id, random_id]
RandomIdAssigned { who: T::AccountId, random_id: H256 },
/// account linked to identity
AccountLinked {
who: T::AccountId,
identity: IdtyIdOf<T>,
},
/// account unlinked from identity
AccountUnlinked(T::AccountId),
}
// CALLS //
#[pallet::call]
impl<T: Config> Pallet<T> {
/// unlink the identity associated with the account
#[pallet::call_index(0)]
#[pallet::weight(<T as pallet::Config>::WeightInfo::unlink_identity())]
pub fn unlink_identity(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
Self::do_unlink_identity(who);
Ok(().into())
}
}
// INTERNAL FUNCTIONS //
impl<T: Config> Pallet<T> {
/// unlink account
pub fn do_unlink_identity(account_id: T::AccountId) {
// no-op if account already linked to nothing
frame_system::Account::<T>::mutate(&account_id, |account| {
if account.data.linked_idty.is_some() {
Self::deposit_event(Event::AccountUnlinked(account_id.clone()));
}
account.data.linked_idty = None;
})
}
/// link account to identity
pub fn do_link_identity(account_id: T::AccountId, idty_id: IdtyIdOf<T>) {
// no-op if identity does not change
if frame_system::Account::<T>::get(&account_id)
.data
.linked_idty
!= Some(idty_id)
{
frame_system::Account::<T>::mutate(&account_id, |account| {
account.data.linked_idty = Some(idty_id);
Self::deposit_event(Event::AccountLinked {
who: account_id.clone(),
identity: idty_id,
});
})
}
}
}
// HOOKS //
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
// on initialize, withdraw account creation tax
fn on_initialize(_: T::BlockNumber) -> Weight {
let mut total_weight = Weight::zero();
for account_id in PendingNewAccounts::<T>::iter_keys()
......@@ -251,6 +324,17 @@ pub mod pallet {
}
}
// implement account linker
impl<T> pallet_identity::traits::LinkIdty<T::AccountId, IdtyIdOf<T>> for Pallet<T>
where
T: Config,
{
fn link_identity(account_id: T::AccountId, idty_id: IdtyIdOf<T>) {
Self::do_link_identity(account_id, idty_id);
}
}
// implement on filled randomness
impl<T> pallet_provide_randomness::OnFilledRandomness for Pallet<T>
where
T: Config,
......@@ -272,13 +356,14 @@ where
}
}
// implement accountdata storedmap
impl<T, AccountId, Balance>
frame_support::traits::StoredMap<AccountId, pallet_balances::AccountData<Balance>> for Pallet<T>
where
AccountId: Parameter
+ Member
+ MaybeSerializeDeserialize
+ core::fmt::Debug
+ Debug
+ sp_runtime::traits::MaybeDisplay
+ Ord
+ Into<[u8; 32]>
......@@ -290,11 +375,11 @@ where
+ Default
+ Copy
+ MaybeSerializeDeserialize
+ core::fmt::Debug
+ Debug
+ codec::MaxEncodedLen
+ scale_info::TypeInfo,
T: Config
+ frame_system::Config<AccountId = AccountId, AccountData = AccountData<Balance>>
+ frame_system::Config<AccountId = AccountId, AccountData = AccountData<Balance, IdtyIdOf<T>>>
+ pallet_balances::Config<Balance = Balance>
+ pallet_provide_randomness::Config,
{
......@@ -307,7 +392,7 @@ where
f: impl FnOnce(&mut Option<pallet_balances::AccountData<Balance>>) -> Result<R, E>,
) -> Result<R, E> {
let account = frame_system::Account::<T>::get(account_id);
let was_providing = account.data != T::AccountData::default();
let was_providing = !account.data.free.is_zero() || !account.data.reserved.is_zero();
let mut some_data = if was_providing {
Some(account.data.into())
} else {
......@@ -346,3 +431,71 @@ where
Ok(result)
}
}
// ------
// allows pay fees with quota instead of currency if available
impl<T: Config> OnChargeTransaction<T> for Pallet<T>
where
T::InnerOnChargeTransaction: OnChargeTransaction<
T,
Balance = <CurrencyOf<T> as Currency<T::AccountId>>::Balance,
LiquidityInfo = Option<<CurrencyOf<T> as Currency<T::AccountId>>::NegativeImbalance>,
>,
{
type Balance = BalanceOf<T>;
type LiquidityInfo = Option<<CurrencyOf<T> as Currency<T::AccountId>>::NegativeImbalance>;
fn withdraw_fee(
who: &T::AccountId,
call: &T::RuntimeCall,
dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
fee: Self::Balance,
tip: Self::Balance,
) -> Result<Self::LiquidityInfo, TransactionValidityError> {
// does not change the withdraw fee step (still fallback to currency adapter or oneshot account)
T::InnerOnChargeTransaction::withdraw_fee(who, call, dispatch_info, fee, tip)
}
fn correct_and_deposit_fee(
who: &T::AccountId,
dispatch_info: &DispatchInfoOf<T::RuntimeCall>,
post_info: &PostDispatchInfoOf<T::RuntimeCall>,
corrected_fee: Self::Balance,
tip: Self::Balance,
already_withdrawn: Self::LiquidityInfo,
) -> Result<(), TransactionValidityError> {
// in any case, the default behavior is applied
T::InnerOnChargeTransaction::correct_and_deposit_fee(
who,
dispatch_info,
post_info,
corrected_fee,
tip,
already_withdrawn,
)?;
// if account can be exonerated, add it to a refund queue
let account_data = frame_system::Pallet::<T>::get(who);
if let Some(idty_index) = account_data.linked_idty {
T::Refund::request_refund(who.clone(), idty_index, corrected_fee.saturating_sub(tip));
}
Ok(())
}
}
// implement identity event handler
impl<T: Config> pallet_identity::traits::OnIdtyChange<T> for Pallet<T> {
fn on_idty_change(idty_id: IdtyIdOf<T>, idty_event: &IdtyEvent<T>) -> Weight {
match idty_event {
// link account to newly created identity
IdtyEvent::Created { owner_key, .. } => {
Self::do_link_identity(owner_key.clone(), idty_id);
}
IdtyEvent::Confirmed
| IdtyEvent::Validated
| IdtyEvent::ChangedOwnerKey { .. }
| IdtyEvent::Removed { .. } => {}
}
// TODO proper weight
Weight::zero()
}
}
......@@ -21,8 +21,8 @@ use sp_core::H256;
use sp_runtime::traits::Zero;
// see `struct AccountData` for details in substrate code
#[derive(Clone, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
pub struct AccountData<Balance> {
#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] // Default,
pub struct AccountData<Balance, IdtyId> {
/// 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>,
......@@ -32,9 +32,26 @@ pub struct AccountData<Balance> {
pub(super) reserved: Balance,
// see Substrate AccountData
fee_frozen: Balance,
/// an optional pointer to an identity
// used to know if this account is linked to a member
// used in quota system to refund fees
pub linked_idty: Option<IdtyId>,
}
// explicit implementation of default trait (can not be derived)
impl<Balance: Zero, IdtyId> Default for AccountData<Balance, IdtyId> {
fn default() -> Self {
Self {
linked_idty: None,
random_id: None,
free: Balance::zero(),
reserved: Balance::zero(),
fee_frozen: Balance::zero(),
}
}
}
impl<Balance: Zero> AccountData<Balance> {
impl<Balance: Zero, IdtyId> AccountData<Balance, IdtyId> {
pub fn set_balances(&mut self, new_balances: pallet_balances::AccountData<Balance>) {
self.free = new_balances.free;
self.reserved = new_balances.reserved;
......@@ -44,8 +61,10 @@ impl<Balance: Zero> AccountData<Balance> {
// convert Duniter AccountData to Balances AccountData
// needed for trait implementation
impl<Balance: Zero> From<AccountData<Balance>> for pallet_balances::AccountData<Balance> {
fn from(account_data: AccountData<Balance>) -> Self {
impl<Balance: Zero, IdtyId> From<AccountData<Balance, IdtyId>>
for pallet_balances::AccountData<Balance>
{
fn from(account_data: AccountData<Balance, IdtyId>) -> Self {
Self {
free: account_data.free,
reserved: account_data.reserved,
......@@ -57,8 +76,8 @@ impl<Balance: Zero> From<AccountData<Balance>> for pallet_balances::AccountData<
#[derive(Clone, Decode, Default, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)]
#[cfg_attr(feature = "std", derive(serde::Deserialize, serde::Serialize))]
pub struct GenesisAccountData<Balance> {
pub struct GenesisAccountData<Balance, IdtyId> {
pub random_id: H256,
pub balance: Balance,
pub is_identity: bool,
pub idty_id: Option<IdtyId>,
}
......@@ -25,10 +25,22 @@ pub trait WeightInfo {
fn on_initialize_no_balance(i: u32) -> Weight;
fn on_filled_randomness_pending() -> Weight;
fn on_filled_randomness_no_pending() -> Weight;
fn unlink_identity() -> Weight;
}
// Insecure weights implementation, use it for tests only!
impl WeightInfo for () {
/// Storage: System Account (r:1 w:0)
/// Proof: System Account (max_values: None, max_size: Some(126), added: 2601, mode: MaxEncodedLen)
fn unlink_identity() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `3591`
// Minimum execution time: 95_130_000 picoseconds.
Weight::from_parts(110_501_000, 0)
.saturating_add(Weight::from_parts(0, 3591))
.saturating_add(RocksDbWeight::get().reads(1))
}
// Storage: Account PendingNewAccounts (r:1 w:0)
// Storage: ProvideRandomness RequestIdProvider (r:1 w:1)
// Storage: ProvideRandomness RequestsIds (r:1 w:1)
......
......@@ -107,8 +107,10 @@ pub mod pallet {
#[pallet::error]
pub enum Error<T, I = ()> {
/// Identity not allowed to claim membership
IdtyNotAllowedToClaimMembership,
/// Not enough certifications received to claim membership
NotEnoughCertsToClaimMembership,
/// Distance has not been evaluated positively
DistanceNotOK,
/// Identity not allowed to request membership
IdtyNotAllowedToRequestMembership,
/// Identity not allowed to renew membership
......@@ -268,9 +270,12 @@ impl<T: Config<I>, I: 'static> sp_membership::traits::CheckMembershipCallAllowed
fn check_idty_allowed_to_claim_membership(idty_index: &IdtyIndex) -> Result<(), DispatchError> {
let idty_cert_meta = pallet_certification::Pallet::<T, I>::idty_cert_meta(idty_index);
ensure!(
idty_cert_meta.received_count >= T::MinCertForMembership::get()
&& T::IsDistanceOk::is_distance_ok(idty_index),
Error::<T, I>::IdtyNotAllowedToClaimMembership
idty_cert_meta.received_count >= T::MinCertForMembership::get(),
Error::<T, I>::NotEnoughCertsToClaimMembership
);
ensure!(
T::IsDistanceOk::is_distance_ok(idty_index),
Error::<T, I>::DistanceNotOK,
);
Ok(())
}
......@@ -279,10 +284,13 @@ impl<T: Config<I>, I: 'static> sp_membership::traits::CheckMembershipCallAllowed
fn check_idty_allowed_to_renew_membership(idty_index: &IdtyIndex) -> Result<(), DispatchError> {
if let Some(idty_value) = pallet_identity::Pallet::<T>::identity(idty_index) {
ensure!(
idty_value.status == IdtyStatus::Validated
&& T::IsDistanceOk::is_distance_ok(idty_index),
idty_value.status == IdtyStatus::Validated,
Error::<T, I>::IdtyNotAllowedToRenewMembership
);
ensure!(
T::IsDistanceOk::is_distance_ok(idty_index),
Error::<T, I>::DistanceNotOK,
);
} else {
return Err(Error::<T, I>::IdtyNotFound.into());
}
......@@ -333,7 +341,7 @@ where
impl<T: Config<I>, I: 'static> pallet_identity::traits::OnIdtyChange<T> for Pallet<T, I> {
fn on_idty_change(idty_index: IdtyIndex, idty_event: &IdtyEvent<T>) -> Weight {
match idty_event {
IdtyEvent::Created { creator } => {
IdtyEvent::Created { creator, .. } => {
if let Err(e) = <pallet_certification::Pallet<T, I>>::do_add_cert_checked(
*creator, idty_index, true,
) {
......
......@@ -127,13 +127,12 @@ impl pallet_identity::Config for Test {
type IdtyData = ();
type IdtyNameValidator = IdtyNameValidatorTestImpl;
type IdtyIndex = IdtyIndex;
type AccountLinker = ();
type IdtyRemovalOtherReason = IdtyRemovalWotReason;
type NewOwnerKeySigner = UintAuthorityId;
type NewOwnerKeySignature = TestSignature;
type Signer = UintAuthorityId;
type Signature = TestSignature;
type OnIdtyChange = DuniterWot;
type RemoveIdentityConsumers = ();
type RevocationSigner = UintAuthorityId;
type RevocationSignature = TestSignature;
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
#[cfg(feature = "runtime-benchmarks")]
......
......@@ -21,8 +21,8 @@ use codec::Encode;
use frame_support::instances::{Instance1, Instance2};
use frame_support::{assert_noop, assert_ok};
use pallet_identity::{
IdtyName, IdtyStatus, NewOwnerKeyPayload, RevocationPayload, NEW_OWNER_KEY_PAYLOAD_PREFIX,
REVOCATION_PAYLOAD_PREFIX,
IdtyIndexAccountIdPayload, IdtyName, IdtyStatus, RevocationPayload,
NEW_OWNER_KEY_PAYLOAD_PREFIX, REVOCATION_PAYLOAD_PREFIX,
};
use sp_runtime::testing::TestSignature;
......@@ -123,7 +123,7 @@ fn test_smith_member_cant_change_its_idty_address() {
run_to_block(2);
let genesis_hash = System::block_hash(0);
let new_key_payload = NewOwnerKeyPayload {
let new_key_payload = IdtyIndexAccountIdPayload {
genesis_hash: &genesis_hash,
idty_index: 3u32,
old_owner_key: &3u64,
......@@ -495,7 +495,7 @@ fn test_certification_expire() {
// Alice can not claim her membership because she does not have enough certifications
assert_noop!(
Membership::claim_membership(RuntimeOrigin::signed(1)),
pallet_duniter_wot::Error::<Test, Instance1>::IdtyNotAllowedToClaimMembership
pallet_duniter_wot::Error::<Test, Instance1>::NotEnoughCertsToClaimMembership
);
// --- BLOCK 23 ---
......
......@@ -107,8 +107,8 @@ fn create_identities<T: Config>(i: u32) -> Result<(), &'static str> {
benchmarks! {
where_clause {
where
T::NewOwnerKeySignature: From<sp_core::sr25519::Signature>,
T::RevocationSignature: From<sp_core::sr25519::Signature>,
T::Signature: From<sp_core::sr25519::Signature>,
T::Signature: From<sp_core::sr25519::Signature>,
T::AccountId: From<AccountId32>,
T::IdtyIndex: From<u32>,
}
......@@ -161,7 +161,7 @@ benchmarks! {
// Change key a first time to add an old-old key
let genesis_hash = frame_system::Pallet::<T>::block_hash(T::BlockNumber::zero());
let new_key_payload = NewOwnerKeyPayload {
let new_key_payload = IdtyIndexAccountIdPayload {
genesis_hash: &genesis_hash,
idty_index: account.index,
old_owner_key: &account.key,
......@@ -176,7 +176,7 @@ benchmarks! {
// The sufficients for the old_old key will drop to 0 during benchmark
let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
let genesis_hash = frame_system::Pallet::<T>::block_hash(T::BlockNumber::zero());
let new_key_payload = NewOwnerKeyPayload {
let new_key_payload = IdtyIndexAccountIdPayload {
genesis_hash: &genesis_hash,
idty_index: account.index,
old_owner_key: &caller_public,
......@@ -199,7 +199,7 @@ benchmarks! {
// Change key
// The sufficients for the old key will drop to 0 during benchmark
let genesis_hash = frame_system::Pallet::<T>::block_hash(T::BlockNumber::zero());
let new_key_payload = NewOwnerKeyPayload {
let new_key_payload = IdtyIndexAccountIdPayload {
genesis_hash: &genesis_hash,
idty_index: account.index,
old_owner_key: &account.key,
......@@ -265,6 +265,17 @@ benchmarks! {
assert!(sufficient < frame_system::Pallet::<T>::sufficients(&account.key), "Sufficient not incremented");
}
link_account {
let alice_origin = RawOrigin::Signed(Identities::<T>::get(T::IdtyIndex::one()).unwrap().owner_key);
let bob_public = sr25519_generate(0.into(), None);
let bob: T::AccountId = MultiSigner::Sr25519(bob_public).into_account().into();
let genesis_hash = frame_system::Pallet::<T>::block_hash(T::BlockNumber::zero());
let payload = (
LINK_IDTY_PAYLOAD_PREFIX, genesis_hash, T::IdtyIndex::one(), bob.clone(),
).encode();
let signature = sr25519_sign(0.into(), &bob_public, &payload).unwrap().into();
}: _<T::RuntimeOrigin>(alice_origin.into(), bob, signature)
impl_benchmark_test_suite!(
Pallet,
// Create genesis identity Alice to test benchmark in mock
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment