Implémentation des quotas

J’ai pas mal avancé dans l’implémentation, même si je me heurte à la difficulté des types paramétriques partagés entre des pallets et un type du runtime. Je ne sais toujours pas si c’est une bonne idée d’être parti sur un champ quotas dans IdtyData.

En gros pour l’instant :

  • dans correct_and_deposit_fee on vérifie seulement si account_data.linked_idty est défini pour ajouter à la queue
  • dans on_idle on appelle process_refund_queue
  • on y appelle spend_quotas qui cherche l’identité dans le storage et vérifie son statut avant d’appeler update_quotas et do_spend_quotas sur les IdtyData
  • ensuite, tout le traitement “quotas” est fait dans IdtyData qui est un type du runtime fourni à la pallet identity comme type paramétrique car il partage des informations avec la pallet universal dividend
  • c’est ensuite try_refund appelé dans process_refund_queue qui retirera les sous à la trésorerie et les mettra sur le compte (non implémenté)

Et la difficulté, c’est de faire en sorte que tout le monde utilise le même type Balance sans mettre des dépendances dans tous les sens avec le trait Currency :

  • IdtyData (type du runtime) dans son champ quotas
  • le trait IdtyQuotasLinker de la pallet identity qui permet à la pallet account de déléguer spend_quotas
  • le trait Quotas de la pallet identity implémenté par IdtyData qui gère update_quotas et do_spend_quotas

Comme IdtyData est un type du runtime, il connaît les types concrets et ne peut pas utiliser des types paramétriques (sinon il y a un cycle de dépendance entre le type runtime et le type idtydata), mais comme il prend des arguments venant de fonctions des pallets, il y a une incompatibilité.

Donc soit on fait une conversion à un moment donné (Balance / BlockNumber → u64 / u32) puis (u64 / u32 → Balance / BlockNumber), soit on déplace le champs quotas vers IdtyValue, mais ça implique quand même d’ajouter une dépendance de la pallet identity à la pallet balance ou un type paramétrique qui implémente le trait Currency.

Bref, voilà les réflexions que je dois avoir en ce moment :no_mouth:


[edit] je pars sur la conversion, c’est ce qui me paraît le plus naturel et ça ne devrait pas poser de problèmes (à tester)

2 Likes

Je ne sais pas si ça peut t’aider, mais dans une vidéo de l’équipe Substrate que j’ai vu il y a quelques temps, il y avait une partie sur “Share functionality between pallets” où l’intervenant revient sur les 2 principaux modes selon lui :

  • couplage fort (par héritage = dépendance direct du trait Config d’une autre pallet)
  • couplage souple (ajout d’un type qui sera implémenté à la définition du Runtime)

Le fait qu’IdtyData soit un type du Runtime (je comprends : pas un type d’une palette) ça signifie que c’est encore autre chose, n’est-ce pas ? Y a-t-il une contrainte qui explique qu’il soit défini comme ça ?

Je suis moins à l’aise avec les explications par vidéo qu’avec la documentation écrite et celle-ci : https://docs.substrate.io/build/pallet-coupling/ me convient bien.

On y retrouve bien les deux types de couplage sous le nom “Tightly coupled pallets” / “Loosely coupled pallets”.

Il faudrait que j’illustre le schéma de couplage des pallets dans Duniter-v2s qui m’a l’air cohérent. Pour l’instant duniter-account et fortement couplé avec balances provide_randomness et pallet_treasury (pallets/duniter-account/src/lib.rs#L52-L56). Je propose d’ajouter des couplages faibles par l’intermédiaire des types IdtyId, InnerOnChargeTransaction, Currency, IdtyLinker. Le type IdtyLinker implémente le trait IdtyQuotasLinker (noms à revoir) qui permet de demander un traitement externe des quotas.

Pour l’instant j’ai fait en sorte que ce soit la pallet identity qui implémente la gestion des quotas parce que notre notion de quotas est fortement liée à la notion d’identité. Et cette pallet n’a pas besoin d’être liée fortement à la pallet universal-dividend parce que la seule donnée qu’elle partage avec elle est dans IdtyData : le first_eligible_ud, d’où le couplage faible à ce type (pallets/identity/src/lib.rs#L79).

Maintenant la question : « pourquoi IdtyData est un type du runtime (et donc pas un type d’une pallet) ? ». Ça rentre dans “couplage souple”, mais au lieu d’utiliser un type implémenté par une pallet (par exemple le type AccountData (runtime/common/src/pallets_config.rs#L70), on utilise un type implémenté par le runtime (configuré runtime/common/src/pallets_config.rs#L445, défini runtime/common/src/entities.rs#L75-L79).

J’imagine que l’idée est de permettre à plusieurs pallets d’ajouter des données liées à une identité dans les IdtyData sans avoir à modifier la pallet identity elle-même (les IdtyValue). Et ça tombe bien parce que c’est ce dont j’ai besoin pour les quotas. Est-ce qu’on a d’autre exemples de ça dans le runtime polkadot ? Il faudrait que je l’étudie plus en détail.

Je pense que c’est un reste du travail d’elois pour avoir beaucoup de modularité, et ça aide pas mal au niveau des tests. Est-ce que c’est un peu “sur-ingéniéré” ? Je n’ai pas encore assez de recul là dessus, je verrai en avançant dans l’implémentation, mais pour l’instant le code que j’écris m’a l’air lisible et à sa place, on verra à la relecture :slight_smile:

5 Likes

En écrivant les tests, je me rends compte que ce serait effectivement plus simple de regrouper les fonctionnalités des quotas dans une pallet.

Du coup la solution que je retiens est de conserver le linked_idty dans duniter-account (c’est bien sa place) et de partir sur une pallet quotas avec une map IdtyId(last_use, amount).

Je laisse ma branche actuelle hugo-quotas-2 de la MR !183 en l’état, et je repars sur une branche hugo-quotas avec une pallet quotas. C’est un peu de travail de réécriture sur quelque chose qui pourrait marcher en l’état, mais c’est plus propre et ça m’embêterait de partir sur une solution trop crade.

2 Likes

C’est bon, les quotas ont leur pallet ! Maintenant que c’est fait, il m’apparaît évident que c’est la bonne approche. J’ai pu développer facilement des tests unitaires, ce qui était trop compliqué avant, et les tests d’intégration fonctionnent toujours. Et en bonus, ça va m’aider à comprendre le point toujours mystérieux (Comportement actuel des frais) puisque dans l’opération, le montant des frais est resté de 2, mais le montant du remboursement est passé à 1 :thinking:

bonus

J’aurais pu implémenter ça plus rapidement si je n’avais pas posé quelques demies journées de congés pour cause de grasse matinée car couché tard et tonne de boulot militant pour la BASE et la lutte contre l’A69.

6 Likes

Une question intéressante de spécifications liée aux quotas a été soulevée dans les discussions sur la MR.

À partir de quand a-t-on droit aux quotas ? Quand l’identité est :

  • créée ?
  • confirmée ?
  • validée ?

Est-ce qu’une identité qui perd le statut de membre perd aussi le droit au quotas (= remboursement des frais de transaction) ?

La seule chose dont on veut s’assurer est que seules les personnes physiques puissent avoir un quota, et qu’elles puissent en avoir un seul.

Seul l’état de membre permet de satisfaire cette contrainte, donc je dirais que seuls les membres ont droit au quota.

À partir d’une identité membre je peux créer plusieurs identités factices qui ne doivent pas être éligibles. Si je perds mon statut de membre pour cause de mort ou de suspicion de multicompte, mes comptes ne doivent plus être éligibles.

Si un jour les identités membres faibles (reçoit le DU mais n’a pas de part de gouvernance et ne certifie pas) sont implémentées, elles pourraient tout de même avoir un quota.

3 Likes

Pour moi, le droit au DU et le droit à l’utilisation gratuite de la blockchain ne sont pas aussi sensibles. Si quelqu’un essaye de profiter plusieurs fois des quotas gratuits en créant plusieurs identités il est hors licence et s’expose à perdre le droit au DU. Il faudra voir les ordres de grandeur des quotas et des frais, mais la menace d’accaparement des ressources blockchain n’est a priori pas présente, les quotas sont juste là pour que les identités n’aient pas à payer de frais de transaction.

Scénario “normal”

En émettant une première certification vers un compte, un membre s’engage à ce que ce compte corresponde à une personne physique, et lui attribue un numéro d’identité. Il me semble logique que dans ce cas, la personne ait droit à l’utilisation gratuite de la blockchain par cooptation.

Scénario d’attaque

Un utilisateur membre souhaitant nuire à la Ğ1 en utilisant des ressources blockchain utilise ses 100 certifications pour créer 100 nouvelles identités sybil, au rythme de une tous les cinq jours. Ces comptes peuvent soumettre des transactions sur le réseau et se faire rembourser les frais correspondant dans la limite des quotas (par exemple l’équivalent de 1 Ğ1 par semaine). Les identités sybil expirent au bout de 2 mois car elles n’ont pas eu d’autres certifications et perdent le droit au quota.
Au total, en 500 jours, 100 identités ont bénéficié de 8 semaines de quotas, soit un équivalent de 800 Ğ1 d’économie de frais de transactions pour l’attaquant. Quelqu’un qui souhaiterait investir 800 Ğ1 dans une attaque aurait le même pouvoir de nuisance, mais sans risquer de perdre le droit au DU.

Je rappelle que les frais de transaction sont un ajout par rapport à la Ğ1v1. Moins l’utilisateur légitime les ressent, mieux ce sera. Ils sont vraiment là pour des raisons techniques, pour que monopoliser les ressources blockchain coûte cher. Et “cher” peut se compter en Ğ1 mais également en certifications hors licence.

3 Likes

Je pense qu’une identité confirmée peut bénéficier de quotas.
Peut-on imaginer que cette identité pas encore membre ait un quota inférieur ?
Peut-on imaginer que chaque certificateur d’une identité non membre transmette une partie de ses quotas jusqu’à ce que l’identité certifiée devienne membre ?

Ok en effet le risque de sécurité est assez faible.

Si on crée un moyen de tricher facilement il faut en revanche vérifier que ça n’augmente pas trop le flicage inter-membres : tout le monde sera invité à se demander quels sont les critères pour déterminer le seuil de bizarreries dans les historiques de certifications et de transactions des gens, pour savoir s’il y a usage illégitime du quota. Une chose de plus que la licence devra imposer.

Cette interdiction pourrait ne pas être dans la licence parce que c’est marginal et qu’il faudrait être tordu pour exploiter ça, mais alors certains pourraient le faire en pensant que c’est légitime.

A priori quand un utilisateur émet une certification (a fortiori la première), on lui demande de s’assurer qu’il a en face de lui une personne physique qui respecte la licence et des règles de sécurité. Donc c’est à voir côté client. Évidemment on peut toujours tricher, mais ce sera difficile d’affirmer ne pas savoir si c’est clair dans les clients grand public.

Il ne faut pas non plus se fier aux développeurs de clients pour respecter des “bonnes pratiques” floues. Si on veut imposer aux clients de décourager cette pratique il faut que ce soit précisé dans la licence, qui est le seul document devant faire consensus et autorité même parmi les non-devs.

Il existe déjà des clients Gchange non-libres faits par des gens n’ayant jamais mis les pieds sur ce forum… il y aura forcément des clients G1 qui feront n’importe quoi, et les membres devront être capables de détecter qu’ils ne respectent pas la licence ou incitent à l’enfreindre.

1 Like

Ce que l’on peut faire : comme le suggère Hugo présumer légitime une identité confirmée, et donc lui accorder le droit aux quotas.

Puis, si l’on se rend compte à l’analyse de la blockchain qu’il existe un abus manifeste sur cette fonctionnalité, on pourrait rajouter des pénalités aux créateurs d’identités qui expirent (ou ne reçoivent qu’une seule certification, etc.) de sorte à rendre la pratique inintéressante.

Il ne faudrait pas non plus inciter à la création de fausses identités, au-delà de la ressource blockchain, cette action doit rester non-banale et engager la responsabilité morale de son auteur.

1 Like