Sans frais de transaction, comment résister aux attaques

J’ai créé le ticket#1242 avec priorité#0 (cc @elois) à cet effet car oui, cette faille devient particulièrement urgente avec le nombre de membres qui augmente constamment. Tôt ou tard les ennemis vont se montrer, mieux vaudra avoir colmaté ce genre de faille avant leur arrivée.


Si, c’est nécessaire de les interdire passé une certaine longueur (par exemple 5), sinon un spammeur peut faire artificiellement grossir la blockchain jusqu’au buffer overflow et détruire la monnaie. En effet il peut, à l’aide de 2 comptes qu’il possède, chaîner des transactions ping-pong et les soumettre au réseau. Au premier bloc il peut atteindre la taille max du bloc, et s’il continue la blockchain va augmenter la taille des blocs de 10%, puis 10%, puis 10%, … c’est juste une énorme erreur de ma part de ne pas avoir codé cette règle plus tôt. Il faut cette limite, rapidement.

Encore une autre suggestion : prioriser les transactions qui réduisent le nombre de sources.

4 Likes

La limite de bloc pourrait monter de façon logarithmique.

Sinon il faudrait mettre une limite en effet. Dans quel cas on a besoin de publier une suite de transactions dans un même bloc ? Je vois une utilité dans le cadre des smart contracts, mais pas avec de simples transactions à première vue.

Quand tu fais du change par exemple : besoin de rassembler des sources avant dépense car trop nombreuses. Ça arrive très régulièrement déjà dans la Ğ1.

D’accord je vois. 5 me semble largement suffisant du coup :slight_smile:

Oui et l’augmentation logarithmique me parait bien aussi.

Bonjour @regisg

concernant le spam j’y ai mis mon grain de sel :slight_smile:


sinon pour la consommation des sources,
il est envisageable de se mettre d’accord coté application client pour qu’une transaction consomme l’ensemble des sources verrouillées par simple signature. *

ce qui peut d’une certaine manière eviter le spam dans le sens où il faut attendre que duniter traite et valide en blockchain la transaction et rend "indisponible " pendant ce laps de temps l’accès a ces sources pour de future transaction.

le point positif c’est que ca réduit les sources a une seule pour une future transaction simple signature.
le point négatif c’est que l’on soit contraint d’attendre la validation de la blockchain pour en effectuer une autre qui soit acceptée…

nb: a defaut de le faire sur l’ensemble des sources et pour pallier a ce dernier point,
peut etre faire cette selection <=>nombre de sources a consommées, en proportion du nombre de sources disponibles (toujours dans le cas simple signature) sachant qu’un utilisateur qui n’effectue aucune transaction sur 1 mois va disposer à minima + ou - de 30 sources (1DU par jour)

Quelle différence entre faire plein de petites transactions vers des petits comptes ?

L’attaque sera dans ce cas limitée au nombre de Ğ1 possédées, à raison de 1 Ğ1 par compte. Tandis que dans mon exemple il n’y a aucune limite.

Ceci dit, c’est un fonctionnement “normal”, légitime, tout utilisateur a le droit de répartir ses Ğ1 et les faire circuler. Cela peut s’assimiler à une utilisation plus intensive de la blockchain. Tant que cela n’empêche pas les autres utilisateurs de faire leurs virements, il n’y a aucun problème en fait (à part pour ceux qui stockent la blockchain).

@cgeek ok je veut bien implémenter ça dans la 1.6 mais quelqu’un peut t’il résumé les règles a mettre en place ?

Juste pour éviter toute ambiguïté : je t’ai mis en copie pour que tu suives le problème, car il me semble important. Tu peux le faire si tu veux, mais on en est encore au stade de la réflexion pour l’instant, avec propositions/réfutations.

Il faudra ensuite spécifier cela techniquement dans le document de protocole.

1 Like

merci c’est bien ce qu’il me semblais a la lecture il n’y a pas encore de conclusion précise sur les règles a coder !

Ok alors je résume les idées intéressantes qui se sont dégagés :

  1. Limiter les transactions chainés à une profondeur maximale de 5
  2. Prioriser les transactions qui réduisent le nombre de sources.
  3. Prioriser les transactions a plus gros montant
  4. Fixer des quotas par tranches de montants

Voici donc une proposition de spec :

Créer des quotas par tranche ET par issuer :
Tranche 1 : montant total inférieur à 3 DU courant : maximum 1 transaction par bloc et par issuer
Tranche 2 : montant total inférieur à 10 DU courant : maximum 2 transactions par bloc et par issuer
Tranche 3 : montant total entre 10 et 100 DU courant : maximum 3 transactions par bloc et par issuer
Tranche 4 : montant total supérieur a 100 Du courant : maximum 5 transactions par bloc et par issuer

Lorsqu’une transaction contient plusieurs issuers le compteur de chaque issuer est incrémenté, s’il existe un seul issuer pour lequel le compteur dépasse le quota alors le bloc est invalide.

Chaque transaction compte pour 1, qu’elle soit chainée ou pas. Notez la cohérence avec la profondeur max pour les transactions a gros montant !

Enfin , un attaquant qui tenterai plein de micro-transactions en parallèle serait limité a 1 transaction par clé (sauf a dépasser les 3 DU courant mais ça commence a faire des fonds…).

Point important : pour éviter qu’un spammer ne squate les blocs pour empecher les autres d’échanger, je ne propose aucun limite sur le nombre d’issuers ni sur le nombre de transactions totales, la règle de la taille limite des blocs me semble déjà bien.

Une dernière chose : je serais favorable a ce que l’on limite le nombre max d’input d’une transaction à 64, dans les faits les clients respectent déjà cette règle et chaine au delà de 40 inputs. Idem pour le nombre max d’output, afin d’empêcher une attaque par transactions prenant trop de place dans le blocs.

a noté que ces propositions concernent exclusivement les règles blockchain, et ne remplacent pas le besoin de fixer des quotas par piscine déjà prévu ici : https://git.duniter.org/nodes/typescript/duniter/issues/1152

3 Likes

Quand je parlais de tranches, ce n’était pas par rapport aux montants mais à la taille en octet des transactions. Il n’y a pas de problème de scaling entre une transaction de 1 G1 et une de 10 kG1. Il y en a par contre une entre une transaction de 100 octets et une de 20000. C’est là qu’il doit y avoir un limite, et un système de tranches (ou encore mieux une notion de quota selon une fonction continue de la taille) me semble nécessaire.

EDIT : Après relecture en effet il est plus simple de spammer avec 1 G1 que 10k :stuck_out_tongue: Par contre il faut aussi penser au payload de la transaction, qui ne contient pas uniquement la somme d’argent mais aussi les conditions de dépenses, qui pourraient augmenter de taille avec le RFC2. C’est dans ce cas là que des tranches en poids de transaction me semblent importantes.

Les spec que je propose s’appliquent au protocole actuel. De plus, quelque soit les conditions d’output il y a toujours un montant total d’inputs nécessairement connu et certain au moment de l’écriture de la transaction, alors la seule faille qu’il restera dans le protocole binarisé c’est la possibilité d’un output au script trop gros, dans ce cas le plus simple me semble être d’imposer une taille binaire maximale pour chaque output OU par transaction.

La taille maximale devrait être dynamique et évoluer dans le temps pour ne pas empêcher de nouveaux usages qui pourraient nécessiter de plus gros contrats. On ne peux pas encore savoir tout ce qu’il sera possible de faire avec, alors limitons le uniquement en fonction du l’usage fait.

Ok je ne maitrise pas du tout cette partie, peut tu proposer une spec anti-spam ?

J’ai avait parlé dans la review de la RFC1. On désigne un certain nombre de tranches par poids (en octets) de la transactions. Puis pour chaque bloc on autorise un certain pourcentage du nombre de transactions.

Par exemple, imaginons les tranches 0 à 100 octets, 101 à 300, 301 à 700, 701+.
On peut avoir dans un bloc au maximum respectivement 30, 10, 5, 2 transactions.

L’idée est d’observer la tendance depuis un certain nombre de blocs pour ajuster les bornes des tranches ainsi que leur capacité. Si la tendance depuis disons 50 blocs est une augmentation progressive des transactions lourdes, on peut en autoriser quelques-unes de plus voir déplacer la borne entre les 2 derniers paliers vers le haut.

On a alors les règles suivantes :

  • une tranche est très remplie, on augmente légèrement son nombre max.
  • une tranche est peu remplie: on diminue légèrement son nombre max.
  • deux tranches adjacentes ont un taux de remplissage trop différent, on décale légèrement la borne commune pour diminuer cet écart. Avec ça on pourrait avoir des limites qui s’adaptent automatiquement à l’usage la monnaie à “moyen terme”, tout en bloquant les spams (a court terme).

J’avais déjà présenté ce système pour les poids, mais on peut aussi le proposer pour les montants (en combinaison avec les poids). Pour qu’une transaction soit mise dans un bloc, il faut qu’elle puisse rentrer dans sa tranche de montant et de poids. Les 2 systèmes seraient indépendants pour vraiment refléter l’usage des utilisateurs de la monnaie.

Enfin il faut toujours laisser un peu de place pour l’évolution. Il faudrait définir des limites minimum au nombre de transactions par tranches, ainsi qu’a la taille minimum de chaque tranche; pour qu’il soit toujours possible de publier de gros scripts pour de nouveaux usages.

On peut aussi envisager de faire ces limites non pas au niveau de blocs de 5 mins mais dans une durée plus grande. 5 minutes c’est assez court et une transaction très lourde toutes les 5 minutes pourrait être problématique. On pourrait alors utiliser des nombres a virgule, par exemple 0.5 transactions pour la plus grosse tranche, qui autorise alors uniquement une transaction lourde tous les 2 blocs.

Je pense qu’il y a moyen de faire quelque chose de très propre avec ce système, peut-être as-t-on l’a une belle RFC à rédiger :slight_smile:

2 Likes

Dans les idées intéressantes, je pense qu’on peut ajouter l’augmentation de taille du bloc de façon logarithmique, qu’en penses-tu ? Car il est vrai qu’une augmentation exponentielle telle que permise aujourd’hui n’a d’une part pas beaucoup de sens, et permet une vulnérabilité.

2 Likes

Je pense que c’est a part, c’est cependant une bonne idée, on peut créer y faire un ticket dédié :slight_smile:

Fait : https://git.duniter.org/nodes/typescript/duniter/issues/1244

1 Like