Implémentation de Duniter en Rust?


#1

@elois J’ai trouvé le document décrivant le protocole très utile pour voir les différences entre les cryptomonnaies “classiques” et Duniter.

Comme dit sur dunicord, je vais essayer d’étudier un peu le fonctionnement des logiciels actuellement disponible et développer mon propre client (en Rust). Ca sera bénéfique pour moi tant pour ma compréhension de ce système comme pour mes compétences en programmation, et ça pourra surement l’être pour Duniter avec de possibles “nouvelles façons de faire”. Avoir plusieurs implémentations pourrait aussi fournir un environnement plus robuste et à même de progresser, chaqu’une pouvant s’influencer mutuellement.


Noeud Raspberry pi toujours suffisant pour calculer?
Noeud Raspberry pi toujours suffisant pour calculer?
Attribution du droit a calculer des blocs
#2

Attend ya confusion avec le terme "“client”, si tu parle de refaire une implémentation du serveur duniter, stp évite le terme client ici tout le monde ratache ce terme aux wallets.


#3

Je n’aime pas trop la dénomination de serveur, car pour moi ça reste toujours un client P2P. Je préfère utiliser la dénomination full node (nœud complet) et light node (nœud léger) qui empêche toute confusion il me semble. Mais peu importe.

J’aimerais directement travailler sur une implémentation alternative du serveur Duniter, vu que ça s’approche plus du protocole. Mais c’est quand même un gros morceau et ça serait surement mieux de m’attaquer a quelque chose de plus “simple” comme un Cesium-like.


#4

Sinon ce qu’on se disait avec @cgeek sur le salon c’est que dans une première étape tu pourrais d’abord migrer en Rust les modules c++ de duniter, ainsi nous pourrions directement intégrer dans l’implémentation actuelle de duniter tes modules rust, et ce serait un 1er pas qui t’apprendra déjà plein de choses :slight_smile:


#5

C’est une erreur conceptuelle, en P2p il n’y a ni client ni serveur. Il y a juste des nœuds. Duniter n’est serveur que lorsqu’il répond a des clients (api BMA). Un nœud sans bma n’est donc en effet pas un serveur, mais pas un client non plus :wink:

je t’ai déjà expliquer que cette dénomination est incorrecte dans le cas des monnaies duniter, car les clients/wallets ne sont pas des nœuds, ce sont des applis locales qui soumettent des requêtes a un serveur distant, elle ne possèdent pas la blockchain et ne jouent aucun rôle dans le fonctionnement de la monnaie. (Si tu enlève tout les clients/wallet la monnaie continue de fonctionné).

Pour qu’on se mette d’accord sur les termes je te propose d’employer systématiquement le terme duniter, duniter étant le nom du projet, pas de sa seule implémentation existante. Et quand on parle d’une implémentation spécifique on pourrait parler de duniter-js et duniter-rust par exemple :slight_smile:


#6

En effet ce ne sont pas des clients du coup X)
J’entends nœud comme nœud du réseau du réseau, les “light nodes” étant en périphérie. Je n’insisterai pas plus, ce n’est que mon avis et ça ne servirait pas à grand chose.

Tout à fait d’accord de séparer le nom du projet des implémentations, c’est beaucoup plus clair. Je vais peut-être commencer par porter les dits modules, vu que ça pourrait être directement bénéfique à duniter-js.


#7

Je suis en train de regarder le code de duniter-rs/wotb. Je vois que la classe gérant la WoT ne stocke pas les clés publiques des membres, mais fonctionne avec des ID arbitraires. Est-ce que le code appellant s’occupe de l’association ID <-> Clé publique ? Est-ce qu’il ne serait pas plus simple de directement stocker les clés publiques, ce qui serait aussi plus pratique pour l’implémentation complète en Rust ?

Sinon j’ai du mal à voir comment est fait la communication entre la partie JS en C++. On dirait que c’est effectué par la ligne suivante, mais du coup c’est assez opaque et je ne connait pas la marge de manœuvre que je peux avoir.

const binding_path = binary.find(path.resolve(path.join(__dirname,'./package.json')));

Pourrait-on m’expliquer comment ça fonctionne, ou alors pourrait-il y avoir une compilation du code en .dll (et equivalent sous Unix) et de simples appels en FFI ?

J’ai créé un groupe sur GitHub pour ce projet : https://github.com/duniter-rs
Si des personnes sont intéressées pour participer au dev, ou à m’aider à gérer tout ça (premier groupe/dépots sur GitHub), elles sont les bienvenues. :slight_smile:


#8

Oui tout à fait.

L’idée de wotb est de permettre une exécution la plus rapide et essentielle possible, car les calculs seront rapidement très coûteux (je n’ai pas la complexité algorithmique associée au calcul de distance, quelqu’un pourrait peut-être te la donner ?).

Or la WoT peut être réduite à une suite d’entiers, ce qui permet d’être très rapide car un membre est équivalent à un index entier de tableau.

La communication se réalise par les addons NodeJS, c’est en gros un pont de communication entre NodeJS et des modules C/C++.

Il semble possible d’avoir des ponts similaires (et plus simples j’ai l’impression) pour Rust :

Comme ça, je te conseillerais d’utiliser Neon pour créer un 1er module NodeJS en Rust, puis de l’intégrer dans ton nœud Duniter pour créer une nouvelle commande de test.

Comment créer une commande de test ? En attendant les tutos, tu peux t’inspirer de ce fichier : app/modules/reset.ts.

Tu le copies, puis crée un nouveau fichier avec un autre nom et modifies le contenu pour appeler ton propre module.

Bon bref après c’est du dev, je pense que tu sauras te débrouiller :slight_smile:


#9

Je vais d’abord faire la partie en Rust dans une crate (bibliothèque Rust), puis je ferais un module pour le FFI avec JS.
Pourquoi faire une interface AbstractWot avec une implémentation comme FileWoT qui ouvre temporairement le fichier. Pour ne pas charger la WoT depuis un fichier ou la mémoire, puis utiliser directement la classe WebOfTrust ?


#10

C’est ce qu’on fait, non ?


#11

Oui, mais pourquoi avoir MemoryWoT et FileWoT ? Le premier ne fait que de la délégation, et le 2ème va créer et détruire la structure à chaque fois.


#12

Oui, parfois on n’utilise que la MemoryWoT car on peut lancer duniter avec l’option --memory, qui permet de fonctionner sans disque. C’est aussi le mode utilisé pour la majorité des tests automatisés.

Quant à la version FileWoT, on travaille directement avec le fichier. Alors c’est vrai qu’on se demande pourquoi on lit le fichier en entier à chaque opération : je te dirais que c’était pour faire au plus rapide, et donc qu’il est possible de faire mieux : genre ne travailler qu’en mémoire une fois le fichier chargé.


#13

Je vois. Je pense donc stocker la WoT en mémoire (normal), et faire une fonction pour la charger depuis un fichier en mémoire, jusqu’a qu’on la détruise (donc pas à chaque appel). Ca te conviens ?


#14

Attention à mesurer quelle sera la taille de la WoT en mémoire une fois atteint un max théorique de 10 Millions de membre et une 100aine de liens par membres. :slight_smile:


#15

Il te faudra alors gérer la persistance, et notamment quand le nœud est coupé par un Ctrl+C. Duniter détecte le SIGINT et tente de se fermer proprement, mais j’ai préféré la stratégie du “persiste au plus tôt” pour éviter les décalages blockchain / module de wot.


#16

Je le ferais. Cependant l’implémentation actuelle charge bien toute la WoT en mémoire comme dans mon début d’implémentation. Mais je ne pense pas que ça soit un problème, et si ça le deviens on pourra rajouter un proxy.

La libération de la mémoire est faite automatiquement par Rust s’il y a une interruption, mais justement pas à chaque appel pour éviter de passer son temps à lire dans les fichiers.

Vous voulez que je stocke la WoT sous quel format ? Un format est déjà défini dans le protocole ou c’est un détail de l’implémentation que le reste du code n’utilise pas ?

Sinon à quoi sert l’attribut mIndex de Node ?


#17

Ce que je veux dire c’est qu’en cas de coupure, étant donné que l’on modifie la WoT pendant l’exécution du programme, il te faut t’assurer que le fichier est aussi mis à jour. Car si Duniter dit « le bloc est écrit » et que le module de WoT n’a rien écrit, au prochain démarrage il y aura un déphasage.

C’est un détail laissé à ta discrétion.

Si tu fais un git blame, tu verras que c’est @Beun qui ajouté cela pour accélérer les calculs : https://github.com/duniter/wotb/commit/300aa9c4563192d34a9499afcceac4d7dd9fdf7d


#18

Je vois. Je ferais donc un système de transaction pour mettre à jour la WoT pour assurer l’atomicité que tu recherche.

Tu veux que j’essaye de faire un format compact ?

D’acc. Mais dans le code final (a moins qu’il y ai une version plus récente ailleurs que dans le master) mIndex est privé et jamais utilisé (seulement affecté). Je ne le met pas pour le moment, je le rajouterais si je le vois autre part dans le code. J’ai fait un premier commit avec la struct Node si tu veux jeter un coup d’œil. :slight_smile:


#19

C’est à toi de voir, sur le long terme c’est préférable. Mais donc pour les 2-3 prochaines années tu peux certainement faire ce qui te plaît :slight_smile:

Il y a un accesseur ! Et l’accesseur est utilisé.


#20

… Je suis vraiment aveugle x)