V2s-datapod: Hasura with Deno middleware to store profiles

Cela fait quelques jours que je travail sur une stack Hasura avec Deno en middleware (et un soupçon de rust) dans le but de stocker toutes les données offchain de Duniter v2s.

Pourquoi Deno ? Parcequ’il permet d’interpréter du TypeScript sans avoir à passer par une étape de transpilation.

Voici une instance en prod: https://gdev-datapod.p2p.legal
Mot de passe: come_on_this_is_demo

Vous remarquerez que toutes les données de profiles CS+ sont présentes.

Vous trouverez dans la doc à droite le schema pour la mutation d’update de profile:

updateProfile(
address: String!
avatarBase64: String
city: String
description: String
geoloc: GeolocInput
hash: String!
signature: String!
socials: [SocialInput!]
title: String
): UpdateProfileResponse
updateProfile

J’ai globalement repris le même fonctionnement que les pod cs+ pour cette partie d’upload.
Sauf qu’ici, les datapods sont capables de vérifier les signatures ed25519 et sr25519, de manière transparente.


J’ai passé un certain temps à adapter cette stack pour qu’elle soit facilement installable et switchable du mode dev au mode prod :slight_smile:
J’ai pour celà fait certains choix différents de ceux de Manu pour l’indexer (je n’utilise pas l’image spécial d’hasura-migration-tool par exemple pour la migration des metadatas, j’ai fait mon propre script d’init de l’image. J’ai aussi essayé de faire au plus simple sur le merge des composes, même si je trouve ça toujours un peu chiant d’en avoir plusieurs mergés, avec le script load.sh ça passe).

Pour le mode prod, vous n’avez besoin que de 4 fichier dans un dossier, et de docker:

$ tree -a
.
|-- .env
|-- docker-compose.prod.yml
|-- docker-compose.yml
`-- load.sh

Que vous trouverez sur le git.
Et de lancer la commande:

./load.sh log #pour voir les logs, optionnel

En mode dev cependant vous aurez bien sûr besoin de cloner l’ensemble du dépôt, puis:

./load.sh dev

Laïus au sujet des pods Cs+

Avant ça, j’ai commencé à intégrer la gestion des pods Cs+ Ğ1v1 dans Ğecko gdev. Cela fonctionne très bien pur la lecture des avatar des comptes pré-migration, il suffit de convertir à la volée les pubkey ed25519 en ss58, mais le soucis est pour l’écriture: Ğecko génère des clés sr25519, donc les signatures ne sont pas reconnu par les pod Cs+, et les clés ne sont de toutes façon pas les mêmes…

j’ai retourné le problème dans tous les sens, et hormis le choix de ne générer que des clés ed25519 pour les nouveaux comptes, je n’ai pas de solutions simple pour utiliser ces pod cs+ v1 avec v2s.


Après avoir écris le script de migration des données Cs+ en TypeScript, puis en python, j’ai finalement décidé de l’écrire en Rust pour des raisons de performances (TS était plus lent que python pour la même implémentation à mon grand étonnement…).

Tout est dans le même dépôt, et correctement Dockerisé.


Précision importante

Cette stack est pensée pour être très simple à bootstraper et performante, je suis très satisfait du résultat actuel à ce sujet pour le moment.
Mais il faut garder un chose en tête: Il s’agit d’instance totalement centralisé.
J’ai exploré des pistes pour faire les faires évoluer en instances décetralisés et fédérés, j’ai des pistes avec la réplication postegres (d’ailleurs je sais que @aya travail dessus à son boulo), mais je ne pense pas que ce soit une bonne idée de partir dans cette direction.

Je pense que nous visons tous une implémentation de vraies worker offchain pour Duniter v2s, avec gestions de frais et/ou quota sur les données, probablement basé sur IPFS comme le recommande l’équipe substrate et semble se diriger toutes les solutions à ce sujet.

Ces datapods sont donc une proposition, à vous de voir si vous voulez partir sur une solution centralisé de ce type pour la migration, le temps que des workers offchain soient implémenté, ou partir sur autre chose.
Ca m’aura à minima permis de me faire la main sur Deno, Rust, et Hasura.

6 Likes

Waouh! Encore un petit pas de géant :smile_cat:
Une instance centralisée avec des backups me paraît tout à fait adaptée pour gérer les données de la Ğ1 pendant un moment après la migration jusqu’à ce qu’on développe une solution décentralisée qui nous convienne !
Je serais curieux d’avoir l’avis de @aya sur l’architecture, et j’ai hâte de tester ça pour des commentaires de transaction. J’ai prévu de prendre des vacances du 21 décembre au 4 janvier, mais je sens que je vais pas pouvoir résister à jeter un œil là dessus ><

3 Likes

J’attendais un peu vos retours vis à vis de cet aspect centralisé, car j’aimerais continuer d’avancer sur plusieurs features:

  • gestion des commentaires comme tu l’évoques
  • gestion de la migration des profiles lors d’une migration d’identité en blockchain
  • interdire l’upload de profiles si le solde du compte est null en blockchain (ce que cs+ ne fait pas)
  • ajout des pages cs+ et peut être d’autres types de donnée que j’aurais oublié
  • tester la réplication multi-maitre postgres pour une potentielle décentralisation (ou bien utiliser Cassandra en SGBD qui est plus fait pour ça et qui est aussi compatible avec Hasura!)
  • probablement d’autres choses qui me viendrons en faisant et au fur et à mesure que l’implémente l’api côté client dans gecko)

Mais je ne voudrais pas trop avancer si vous décidez de partir sur une autre solution, j’attends donc un peu plus d’avis avant de continuer.

1 Like

Je suis plutôt d’accord avec Hugo : déjà si l’on peut démarrer la Ğ1v2 avec un outil pour gérer des données offchain, ce sera très bien.

Si l’outil peut être pensé pour devenir décentralisé c’est encore mieux, mais c’est du bonus.

2 Likes

Ok super :slight_smile:

J’ai creuser un peu plus l’aspect décentralisé, et je pense que ça va être compliqué pour cette stack.
La seule façon de faire de la réplication multi maitre sur PostgresSQL est avec BDR3. Le soucis, c’est que depuis BDR2, ce n’est plus open source et réservé au subscribers de l’entreprise 2ndquadrant.

Les autres moyens de faire permettrait au mieux de garantir une haute disponibilité (ce qui serait déjà super),avec le plugin psql Citus par exemple (maintenu par MS mais open source), qui fait du master-slaves. Il y a d’autres solutions pour du master-slaves psql.

Peut être que @vit ou @aya pourront apporter des précisions là dessus.
Et en fait Hasura n’est pas encore compatible avec Cassandra (qui est une stack Java, je préfère éviter perso…).


De mon point de vue, la meilleurs solution pérenne pour des données offchain serait:

  • Déployer un réseau de noeud IPFS (public ou privé, à déterminer) qui auto-pin les données reçus dont la signature crypto correspond à l’adresse émetrice.
  • Le client pousse un JSON des données de profiles mise à jours au réseau IPFS et obtiens le hash correspondant.
  • Ajouter un extrinsic au runtime Duniter qui émet un événement contenant le hash IPFS du profile
    après vérification de l’auteur de cet appel
  • L’indexer subsquid index cet événement de manière à retrouver très rapidement les données offchain.

De cette manière, on aurait une solution simple et efficace selon moi. On pourrait facilement demander des frais ou un dépot Ğ1 en contre partie du stockage, ou pas.

Mais toute la partie substrate sort de mes compétences donc je ne me lancerais pas seul là dessus.
Je vais donc continuer sur ma stack Hasura que je trouve très bien, peut être configurer de la réplication master-slave pour de la haute disponibilité, mais pas plus.

Coucou :slight_smile:

Je n’ai pas eu le temps de vraiment me pencher sur hasura, il faudrait que je m’y intéresse plus sérieusement. Et encore moins sur deno que je ne connais absolument pas ! Donc je ne me risquerai pas à commenter le choix des outils, je te fais confiance :slight_smile:

J’ai regardé le code, pour ce que j’en comprends je trouve ça bien et l’intégration docker est top j’aurais pas fait mieux :stuck_out_tongue:

Après, question architecture…

Je te rejoins sur tes premières précisions, il s’agit d’une solution centralisée et tu ne pourras pas la faire évoluer vers quelque chose de décentralisé. Tu vas te retrouver avec les mêmes problématiques que pour la réplication elasticsearch et tu devras limiter les réplicas à des personnes de confiance pour éviter qu’on vienne écrire en base sans passer par ton middleware qui vérifie les signatures.
Donc postgres master/slave pour de la HA, oui (avec docker le plus simple c’est du repmgr en streaming replication, sinon tu peux aussi tester patroni ou stolon), mais pas de fédération et encore moins de master/master à grande échelle, ça va être une usine à pb.

Pour une infra décentralisée, il faudra une solution p2p. Et là encore, je te rejoins sur IPFS.
J’ai recensé pas mal de solutions qui peuvent répondre au besoin, mais ma préférence va toujours vers IPFS. Je rêve du jour où on pourra valoriser du stockage IPFS avec de la Ğ1 :slight_smile:

Pour stocker et auto-pin les données offchain, on peut créer un cluster ipfs (public :p) ala filecoin.
N’importe qui pourrait participer au cluster et se voir rémunéré en Ğ1 contre le pin de données.

Je ne connais pas encore le fonctionnement de v2s mais pour garantir l’authenticité des data dans ipfs, j’avais commencé un POC en python pour utiliser la toile de confiance de duniter v1. L’idée c’était :

  • convertir sa clé duniter au format jwk
  • publier sa clé publique dans IPLD
  • publier des “preuves d’appartenances” ucan dans IPLD

On pourrait ainsi authentifier n’importe quel type de contenu de manière décentralisée.

3 Likes

C’est simplement un moteur GraphQL qui génère automatiquement l’API en lisant ta bdd Postgres, c’est très simple à utiliser.
Il y a bien sûr pas mal d’option de personnalisation via la console en ligne, donc tu as le lien avec le mdp dans le sujet si tu veux jeter un oeuil.

Il m’a permis par exemple de créer des computed field, pour convertir à la volé en SQL le champ avatar qui est un BYTEA en base64 pour optimiser l’envoi sur le réseau.

Et bien sûr de créer des actions personnalisé, ce qui permet l’usage d’un middleware pour une mutation graphql custom pour la mise à jours des profiles, pour vérifier la signature, transformer le format des pubkey si nécessaire, ect ect…

Deno c’est en gros une des relèves de node.js (avec Bun), qui permet notamment d’executer du TypeScript directement, et qui simplifie grandement la gestion des modules.

C’est exactement ce que je me dis, le but est de surtout pas sur-complexifier cette stack, car je la vois comme provisoir dans cette écosystème et j’aimerais que ça reste ultra facile à bootstraper et à maintenir. D’où l’usage de Deno en TS, beaucoup plus accessible que du Rust pour des contributions je trouve.

Ok super, je pense que ça vaut le coup de garantir une haute disponibilité du service, car si on y stock les commentaires de transactions par exemple, il va devenir relativement critique pour les clients.

On continue on MP pour voir ça :wink:

Je pense qu’il faut prendre le temps de concevoir ça avec ceux qui bossent sur le coeur de Duniter pour convenir de la meilleur stratégie. On sera curieux de voir ta POC je pense :slight_smile:

Mais je pense qu’il sera nécessaire d’en passer par Duniter pour garder une trace infalsifiable du dernier hash correspondant au profile ou à la donnée, et que ce soit directement indexé par subsquid pour des recherches ultra rapide et complète parmis ces données.

3 Likes

Yep, j’ai merger l’ajout des commentaires de transactions. C’est un premier jet et je ne l’ai pas encore testé côté client, il manque l’étape cruciale de vérification de la conformité de l’id de transaction en blockchain via RPC (ou via squid si ça s’avère trop compliqué via RPC mais je ne pense pas).

J’ai reset le datapod en ligne car depuis, comme on disait, une subscription aux killedAccounts supprime automatiquement les profiles dont les comptes associés sont détruits en blockchain, et surtout le script de migration n’importe pas les wallets non référencés comme actif par squid (ce qui concerne ~50% des profiles Cs+), passant de 2.5Go de DB SQL à 2.1Go.

Pour la suite, grâce aux données de migrations psql générés par Hasura, il ne devrait plus y avoir nécessité de reset ce pod (encore heureux…).


Mais j’avoue que le sujet nostr pose question, sur le papier c’est exactement ce qu’il nous faut pour des donnés offchain décentralisés, et ça rendrait ces datapod immédiatement obsolete.

Mais j’ai l’intuition qu’en commençant à tester, on va se rendre compte que c’est plus compliqué que ça, et que ça nécessite des mois de R&D supplémentaires.
Je ne saisi pas bien le principe des relays qui communiques avec les clients mais qui ne communiques pas entre eux, comment c’est possible dans un réseau décentralisé et unifié… bref je suis un peu tiraillé entre finir ces datapods (en vrai ils le sont quasiment hormis quelques bricoles et la configuration d’une couche de réplication psql master/slave) et me focus sur des tests et approfondissement de nostr.

2 Likes

Je reviens ici dans le cadre de mon expérimentation IPFS pour les datapods v2. Mon idée est de conserver l’architecture Postgres/Hasura, mais de changer la manière de faire rentrer les données : on utiliserait IPFS pour tout plutôt que :

  • un script Rust pour l’import initial depuis un pod Cesium+
  • un endpoint centralisé pour la soumission de nouvelles données

Cela permet de combiner :

  • une soumission et un stockage parfaitement décentralisés indépendants de Duniter
  • une indexation subjective pour exposer une API pratique pour les clients (GraphQL)
2 Likes