Nos derniers échanges concernant la RFC5 développée par @nanocryk montrent que l’évolution de Duniter vers ce protocole va être particulièrement longue. Je redoute un effet tunnel de 2 ans sans évolution même mineure du protocole, ce qui outre le fait de laisser traîner des vulnérabilités sur le protocole v10 met toute la responsabilité des évolutions sur les seules épaules des développeurs de cette RFC5.
Je pense qu’on pourrait pourtant acter le principe d’évolutions successives par forks déclenchés dynamiquement, qu’il s’agisse de hard-forks (HF) ou soft-forks (SF). Par exemple, on pourrait commencer par :
- HF : Évolution qui intègre les déclencheurs
- SF : Évolution de la règle de difficulté personnalisée (sujet porté par @elois)
- HF : Évolution de l’algorithme de preuve de travail (cc @aeris et @Inso)
- HF : Ajout des clés déléguées
- …
- HF : Binarisation des documents
- HF : Scripting simple, 4 fonctions (XHX, SIG, CSV, CLTV)
- HF : Merklarisation des données de blocs
- HF : Scripting avancé
- …
Ce sujet des forks peut être perçu, selon moi, sous 2 aspects liés : fork et déclencheur.
Fork
Il convient d’abord de se mettre d’accord sur les termes, et au sujet des forks ça n’a pas toujours été très clair me concernant. Voici les définitions que je propose, @nanocryk pourra confirmer.
D’abord, il faut bien comprendre que le concept de fork définit ci-après s’évalue toujours du point de vue d’un nœud ou d’un ensemble de nœuds qui intègrent un code nouveau vis-à-vis d’autres nœuds qui n’ont pas ce code nouveau.
Par exemple, si demain nous souhaitions modifier la règle de difficulté personnalisée (point 1. ci-dessus) afin de la rendre plus contraignante alors nous devrions introduire du code nouveau qui viendrait changer le comportement du nœud vis-à-vis des blocs qu’il accepte, et donc changerait son comportement vis-à-vis à des autres nœuds du réseau qui n’ont pas ce code nouveau.
En effet, s’il n’y a pas de nouveau code, ou si le code nouveau ne crée pas de blocs invalides vis-à-vis des autres nœuds du réseau qui n’auraient pas ce nouveau code, alors il n’y a pas de fork.
Ainsi certaines actions peuvent être entreprises sans aucune perturbation du réseau : par exemple, un nouveau code pourrait décider de ne plus inclure les transactions de X ou Y. Personne ne serait obligé d’installer un tel code sur son nœud, c’est néanmoins possible et permet de contrer certaines nuisances d’attaquants.
À noter que j’insiste sur le fait qu’il s’agit simplement de code nouveau, et pas de version de logiciel ou d’autre chose. Bitcoin amalgame un peu tout car il fonctionne en mode tout ou rien : soit il gère le code A soit il gère le code B, mais jamais les deux. Duniter sait gérer A et B en simultané par exemple, ce qui lui permet de rembobiner un fork utilisant B mais avorté, se rebrancher sur la branche utilisant A et recommencer plus tard, de façon automatique. Bitcoin n’a pas du tout cette nuance.
Pour simplifier, je parlerai ici de nouvelle version pour évoquer un logiciel contenant du code nouveau. Donc, une nouvelle version contient aussi l’ancien code. Importante nuance !
Attention ici nous raisonnerons façon Duniter, et donc la notion de hard-fork n’est pas strictement identique à celle que vous pourriez trouver sur le web.
Soft-fork
C’est un fork. Donc, ça crée un véritable instabilité au sein du réseau. Toutefois le soft-fork permet une transition en douceur.
Le soft-fork est le cas où des nœuds en nouvelle version créent des blocs tout à fait acceptés par les nœuds en ancienne version, mais refusent une partie des blocs créés par ces derniers.
En somme le soft-fork restreint les blocs acceptés par le réseau. Il se produit de façon souple et continue, à mesure que les nœuds passent en nouvelle version.
L’évolution 1. ci-dessus constitue typiquement un soft-fork, car son implémentation ne fait que restreindre les possibilités existantes en terme de niveau de preuve de travail requis.
En résumé, dans un soft-fork :
- la compatibilité ascendante est perturbée
- la compatibilité descendante est assurée
N.B. : un soft-fork qui restreint à aucune possibilité est un hard-fork.
Hard-fork Duniter
C’est un fork qui cette fois crée une instabilité sérieuse, beaucoup plus tranchée au sein du réseau.
Le hard-fork est le cas où des nœuds avec une nouvelle version créent des blocs refusés par les nœuds en ancienne version, et réciproquement.
Le hard-fork Duniter est toutefois plus nuancé, puisque le logiciel accepte tout de même les blocs créés par les nœuds en ancienne version jusqu’au point de bascule où le fork prend réellement son envol. Le hard-fork Duniter ne brise la compatibilité descendante qu’à partir de ce moment.
De même, Duniter est capable de générer des blocs en ancienne version tant que la phase active de fork n’a pas été déclenchée, ou si la phase active est avortée temporairement.
En résumé, dans un hard-fork Duniter :
- la compatibilité ascendante est assurée mais vise à être brisée =>
- la compatibilité descendante est assurée mais vise à être brisée =>
Pour comparaison, dans un hard-fork Bitcoin :
- la compatibilité ascendante est brisée
- la compatibilité descendante est brisée
Déclencheur
Historique
Pour le moment, nous n’avons pas expérimenté de soft-fork ni de hard-fork sur la Ğ1. Donc nous n’avons jamais eu besoin d’en déclencher.
Toutefois nous en prévoyons un dans pour la version 1.6 de Duniter, afin d’autoriser les transactions chaînées au sein d’un même bloc. Il s’agit d’un déclencheur temporel (en gros : le code déclenche la règle à partir d’une date) et la compatibilité descendante n’est pas assurée. Nous nous permettons cette exception car il y a peu de risques si la date est suffisamment lointaine pour que la majorité des auteurs de blocs se mettent à jour, et vu le nombre encore peu conséquent, ça devrait le faire.
Pour la suite : flag comme le Bitcoin
Il s’agirait d’implémenter un mécanisme similaire au Bitcoin. Je ne connais pas précisément la mécanique pour ce dernier, toutefois je comprends qu’il existe des champs dans les blocs qui permettent de savoir à quel point le réseau est prêt à activer une fonctionnalité, bien évidemment ceux activant ce flag ayant déjà le code prêt à être exécuté.
Pour ce déclencheur, si l’on veut rester simple dans Duniter alors on pourrait simplement ajouter un champ UpgradeProtocol: true
dans les blocs.
Ajouter un tel champ est bien sûr un hard-fork, mais il s’agirait de la dernière opération de hard-fork sans déclencheur « explicite ». C’est l’évolution 0. que je vous proposais dans la liste au début de ce potst.
Éventuellement dans des versions plus lointaines, nous pourrions aller plus loin et activer des fonctionnalités, encore une fois comme le fait Bitcoin. C’est-à-dire que ce n’est pas du tout ou rien, on peut activer chaque nouvelle fonctionnalité indépendamment sans forcément incrémenter la version de bloc. @nanocryk en sait certainement plus que moi à ce sujet.
Voilà, qu’en pensez-vous ? J’aimerais particulièrement les avis de @nanocryk et @elois qui vont sûrement bosser sur l’implémentation d’un nouveau nœud Duniter, écrit en Rust. L’idée étant qu’en avançant petit à petit, on ne fiche pas tout le travail de RFC5 et de coding Duniter-RS en l’air