Liste des endpoints

Yes c’est une solution !

Mais je viens d’avoir une fulgurance ! En fait il y a beaucoup plus simple ! Sur la couche identify() de libp2p, on peut déclarer tous les protocoles que l’on veut, par exemple aujourd’hui une nœud Substrate déclare tous ceux-là :

[
'/c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3/block-announces/1'
'/gdev/block-announces/1'
'/c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3/grandpa/1'
'/paritytech/grandpa/1'
'/c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3/transactions/1'
'/gdev/transactions/1'
'/ipfs/ping/1.0.0'
'/ipfs/id/1.0.0'
'/ipfs/id/push/1.0.0'
'/c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3/kad'
'/gdev/kad'
'/c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3/state/2'
'/gdev/state/2'
'/c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3/sync/warp'
'/gdev/sync/warp'
'/c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3/sync/2'
'/gdev/sync/2'
'/c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3/light/2'
'/gdev/light/2'
]

Mais on peut bien rajouter ceux que l’on veut, par exemple ceux précisés par les options au lancement de Duniter V2. Ainsi, c’est le nœud qui déclare lui-même “ses APIs”, il peut dire qu’il supporte le protocole “indexeur V2S” sur telle adresse. Bien évidemment derrière ce n’est pas lui mais on s’en fiche, on se sert juste de la couche P2P pour porter l’information.

Bref c’est un gossip naturel porté par libp2p.

Bon, je ferai quelques tests d’ici demain mais ça semble bien plus souple et très facile d’accès !

5 Likes

Pas mal du tout :slight_smile:

Ça ressemble à l’idée que j’avais eu pour annoncer les datapods en utilisant la liste de endpoints annoncés : "Fiches de pair" de datapods. Mais on retombe sur mon défaut de toujours : j’ai beaucoup de mal à me lancer dans l’implémentation tant que je ne suis pas entièrement satisfait de l’architecture :expressionless:

1 Like

Ah oui ça y ressemble beaucoup ! Je creuserai ça !

Bon bah c’est plutôt concluant :slight_smile:

Exemple de endpoint rajouté sur lancement de Duniter avec l’option --public-rpc localhost:8000/ws :

/c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3/duniter-endpoint/1/rpc/localhost:8000/ws

Si je détaille :

  • /c184c4ccde8e771483bba7a01533d007a3e19a66d3537c7fd59c5d9e3550b6c3 : préfixe de la gdev-800
  • /duniter-endpoint/1 : domaine “réservé” à Duniter pour déclarer des endpoints spécifiques. Ici, protocole en version 1 (ça n’a aucune incidence, sauf si demain on veut changer la façon de décrire les endpoints).
  • rpc : protocole exposé (j’ai choisi le nom)
  • localhost:8000/ws indique que l’interface RPC est exposée sur localhost:8000 sur le path /ws

Le résultat est satisfaisant, le code est très simple. Je me suis basé sur la branche hugo/endpoint-gossip qui avait servi a tenter l’approche initiale avec un protocole de gossip (comme indiqué dans ce commentaire du ticket #97).

Je publie une MR dès que tout est propre, en me cantonnant aux options rajoutées par @HugoTrentesaux :

  • --public_rpc <adresse>
    Déclarera un endpoint sous /duniter-endpoint/1/rpc/<adresse>

  • --public-squid <adresse>
    Déclarera un endpoint sous /duniter-endpoint/1/squid/<adresse>

Sauf bien sûr si vous avez d’autres idées ou besoins à formuler.

Dans les détails

J’ai mis un peu de temps à vous confirmer que ça fonctionnait car je me suis perdu dans les méandres de Substrate.

À la base je voulais partir sur une simple configuration de la couche identify() de libp2p, mais Substrate verrouille tout. Cette couche est inaccessible directement.

Substrate propose en revanche deux façons de rajouter des protocoles :

  • les protocoles de notification, dont Grandpa fait partie
  • les protocoles requête/réponse

Initialement je suis parti dans la même direction que Hugo, c’est-à-dire les protocoles de notification. Mais c’est très lourd à coder, car concrètement ces protocoles servent à pousser de manière active des données aux autres nœuds du réseau et Substrate attend de nous un service qui va activement réagir à la découverte de pairs, service qu’il faut donc coder. Faute de quoi, Duniter génère une montagne de logs d’erreurs :

user protocol has exited, exiting

Une fois compris ce point, j’ai tenté de me diriger vers les protocoles requête/réponse pour lesquels nous sommes plutôt “en attente” comme le ferait un serveur.

Et bingo :slight_smile: je n’ai rien codé d’autre qu’une déclaration de protocole.

Branche de brouillon disponible ici.

edit

N.B. : je n’ai pas précisé mais, pour obtenir de chaque nœud la liste des endpoints spécifiques à Duniter que celui-ci propose, il faut s’y connecter (c’est pour ça que je parle de la couche identify()).

On pourrait rajouter un endpoint RPC sur Duniter pour obtenir la liste des endpoints connus du réseau afin de s’éviter d’avoir à le parcourir nous-mêmes.

8 Likes

Super content que cette tentative ratée ait quand même servi à quelque chose :slight_smile:

Plutôt un tiret pour les deux, je vois un underscore pour “public_rpc” ! :mag:

C’est frustrant, hein ?

C’était le sens de #97, et ça éviterait d’avoir à coder la découverte libp2p dans les clients, ce qui est intéressant quand on n’est pas en js (Ğecko, Ginkgo…).

3 Likes

Oui, c’est une typo de ma part sur le forum. Ce seront bien des tirets dans la MR.

Oui car j’ai beau creuser tout le code, la moindre faille, je ne trouve aucun passage. Et pourtant il y a pas mal de code.

Je vais voir ce que je peux faire.

Si le problème bloquant est de passer un item en public dans libp2p, il est possible de remplacer des dépendances transitives par un fork : Overriding Dependencies - The Cargo Book

1 Like

Merci pour le rappel tuxmain, je n’avais plus cette fonctionnalité de Cargo à l’esprit.

Pour l’instant, j’essaie de redonner une chance aux protocoles de notifications : nous avions Grandpa comme exemple, mais celui-ci est assez compliqué. J’ai trouvé dans le code Substrate un autre exemple de gossip mais pour les transactions. Celui-ci semble plus simple. Je creuse.

Merci @cgeek pour cette avancée !

Si je comprends bien, Césium peut se connecter a un noeud et lancer un appel a identify() pour ensuite parser les endpoints, c’est bien ça ? Ou alors j’ai pas compris…
Que me conseillez vous ?

Non finalement ce sera encore plus simple : chaque nœud exposera une méthode (appelée duniter_endpoints probablement) dans son API RPC pour obtenir la liste des endpoints connus.

Je suis encore en train de coder le protocole de gossip, mais quand je teste en local mon développement je fais :

curl -X POST http://localhost:9944 \
    -H 'Content-Type: application/json' \
    --data '{"jsonrpc":"2.0","id":1,"method":"duniter_peerings","params":{}}'

Et j’obtiens une réponse JSON avec la liste des endpoints.

Bon je vous cache pas que le protocole de gossip est assez galère à coder, mais j’en suis à un point où je peux affirmer que ça va fonctionner et que ce sera dispo d’ici 2-3 semaines. :slight_smile: il faudra juste relivrer Duniter.

edit : de ce fait, une fois connecté à une API RPC tu peux faire appel à la méthode duniter_endpoints afin d’obtenir d’autres API RPC du réseau, auxquelles tu peux te connecter pour avoir leur propre vision des endpoints du réseau, etc., jusqu’à avoir une vue qui te satisfasse.

6 Likes

Exemple de réponse à la requête RPC ci-dessus :

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "peers": [
      {
        "peer_id": "12D3KooWHyUiZges2LWtwKjcd8wJzu8LidKR3533Wz4VhBGxt1ux",
        "endpoints": [
          {
            "protocol": "rpc",
            "address": "localhost:8000/ws"
          },
          {
            "protocol": "squid",
            "address": "localhost:8001"
          }
        ]
      },
      {
        "peer_id": "12D3KooWEjx6iQQkGcbD3D5BBDjgCx3f3hpM5SkVjUYF7aAjoUpU",
        "endpoints": [
          {
            "protocol": "squid",
            "address": "localhost:3000/v1/graphql"
          }
        ]
      }
    ]
  }
}

La MR ne va pas tarder à tomber, je dois encore mettre au propre et rajouter quelques tests unitaires, et la MR contiendra pas mal d’explications. Si vous avez des suggestions, c’est encore le moment.

3 Likes

Les résultats n’incluent pas explicitement les protocoles sous-jacents. Comment sait-on si c’est http, https, ws, wss ? Pour RPC il y a la convention du chemin, mais pour squid on ne sait pas.

Est-il possible d’indiquer le protocole explicitement ?

La valeur dans le champ « adresse » est libre, tout comme celle dans le champ « protocole ».

On peut mettre des contraintes, mais je n’y ai pas trouvé d’intérêt. En voyez-vous ?

2 Likes

La MR est prête.

5 Likes

Merci @cgeek pour cette contribution conséquente, je viens de relire ta MR, j’ai laissé quelques commentaires :slight_smile:

2 Likes

Extra ! On a hâte de pouvoir jouer avec :slight_smile:

De ce que je comprends, il faut déclarer son propre noeud RPC même si son adresse est déjà déclaré en tant que public address ?
Ou sa reprend automatiquement la même déclaration pour rpc et rajoute simplement des champs pour squid et les protocoles déclaré dans le json que Elois propose ?

Tu veux dire via l’option --public-address ? Ça c’est pour l’adresse P2P, par pour le RPC. D’où la nécessité d’une option --public-rpc.

1 Like

Super, je viens de voir que ça a été mergé !
Est ce qu’il existe un nœud qui l’implémente? @cgeek

1 Like

Oui ! Je viens juste de livrer la version 0.10.0 du client :partying_face:

curl -X POST https://gdev.cgeek.fr \
  -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","id":1,"method":"duniter_peerings","params":{}}'
{
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "peerings": [
            {
                "peer_id": "12D3KooWGvtbSM9SXMTAukT9zQo26QWegPcvP7pRhPU4HxK151Sx",
                "endpoints": [
                    {
                        "protocol": "rpc",
                        "address": "wss://gdev.cgeek.fr"
                    }
                ]
            }
        ]
    }
}
3 Likes