ZYCORD docs
Français
ZycordDocsFaire tourner un nœud

Faire tourner un nœud

Le guide de l'opérateur pour zycordd. Le protocole qu'il parle est la spécification réseau ; cette page traite de son exploitation.

La version en une ligne#

zycordd --devnet --dir ./devnet

C'est un nœud complet : il valide chaque bloc depuis la genèse, sert des pairs, et répond à un RPC en lecture seule sur 127.0.0.1:9420. Pour rejoindre le réseau qui tourne réellement, voyez Testnet public — et notez qu'il lui faut le binaire -randomx.

Ce que le nœud n'a pas#

Il n'y a jamais aucun matériel de clé dans le processus du nœud. Il n'accepte ni graine, ni phrase secrète, ni fichier de clé. La signature a lieu dans zcd, et le seul point d'entrée en écriture du nœud — /submit — n'accorde aucune autorité : un certificat soumis est validé exactement comme le serait un certificat venant d'un inconnu.

Il n'y a pas non plus de point d'entrée privilégié, parce qu'il n'y a rien à privilégier. Aucune clé ne peut suspendre, mettre à jour, censurer ni frapper de monnaie, il n'y a donc aucun appel à exposer.

Si vous placez le RPC derrière un proxy, limitez le débit au niveau du proxy

Le limiteur s'indexe sur le pair de transport et jamais sur X-Forwarded-For : un limiteur qui fait confiance à un en-tête que le client fixe est un limiteur que le client désactive. Derrière un proxy, chaque requête arrive donc avec l'adresse du proxy, si bien que la limite par client cesse d'être par client et devient un unique seau partagé de 600 par minute pour tout le monde à la fois — et un seul appelant peut le vider pour tous. C'est sans doute pire que pas de limite du tout, et rien dans la réponse ne le dit : les appelants reçoivent un simple 429, qu'ils en soient la cause ou que ce soit quelqu'un d'autre.

La surface en lecture seule#

Tout ce qui observe la chaîne depuis l'extérieur — un explorateur, un moniteur, un indexeur — la lit ici. Le nœud livre des octets et jamais d'interprétation.

Point d'accèsRépond
/status, /headIdentité de la tête, hauteur, racine d'état
/block?height=N ou ?id=0x…Un bloc en JSON, avec canonical, orphaned et confirmations
/block?…&format=sszLe même bloc en octets SSZ canoniques, application/octet-stream
/paramsLe jeu de paramètres actif et sa racine de consensus
/cell, /balanceÉtat de la tête
/feesFrais de base, et les plafonds élastiques en vigueur
/mempoolCompteurs du pool ; ?limit=N ajoute jusqu'à 1000 identifiants en attente, du plus faible au plus élevé
/network, /metricsForme des connexions et compteurs
/submitLa seule écriture, et elle n'accorde rien
Les plafonds ne sont pas des paramètres

Le §8.1 du livre blanc fait des plafonds d'octets, de gas et de certificats du bloc des fonctions de T, la cible séquentielle, et T est un état de consensus que le contrôleur d'époque déplace. /params ne porte donc que les valeurs de genèse — block_byte_limit_genesis et consorts — qui sont le point de départ de T et le plancher vers lequel il peut redescendre, jamais la limite en vigueur. Ces nombres restent réels pour toujours, si bien que lire l'un d'eux comme « la limite de taille de bloc » est faux silencieusement, et faux de jusqu'à l'écart entre la valeur de genèse de 2,5 Mo et le mur de capacité de 8 Mo. /fees sert le T vivant et les quatre plafonds qui en découlent, de sorte que la dérivation peut être vérifiée plutôt que crue.

Pourquoi les octets comptent#

blake3("zcd/block/v1" ‖ header_bytes) est l'identifiant du bloc, si bien qu'un observateur qui redérive les identifiants à partir de ce qui lui a été servi soit s'accorde avec le réseau, soit s'en aperçoit immédiatement. C'est aussi ainsi que l'on obtient les résultats par certificat : savoir si un certificat a été appliqué, omis et facturé, ou rejeté est calculé à l'intérieur du fold et jamais persisté, car une ligne par certificat pour toujours, dans un magasin qui vit en mémoire, n'est pas quelque chose qu'on peut demander à un nœud de porter. Un observateur qui veut les résultats replie le bloc lui-même.

Ce qui n'est pas là, et ne le sera pas#

  • Aucune itération arbitraire sur l'état. Pas de liste d'adresses, pas de balayage par préfixe, pas de liste des plus riches.
  • Aucun état historique. /cell et /balance lisent la tête ; « le solde à la hauteur H » suppose un historique conservé que le nœud ne garde pas.
  • Aucun appel d'administration, de réindexation ou de débogage, parce qu'il n'y a rien à privilégier.

L'agrégation appartient à ce qui observe, calculée une fois à l'ingestion dans sa propre base de données.

Codes de statut#

Les lectures répondent à GET et HEAD et refusent tout autre verbe par un 405. Une question bien formée dont la réponse est négative — une hauteur que la chaîne n'a pas atteinte, un identifiant inconnu, le corps d'un bloc qui a perdu une réorganisation — donne 404 ; seule une requête malformée donne 400. Un enregistrement que le nœud a écrit et qu'il ne peut plus relire donne 500, et non 400 : la faute revient au disque du nœud, et dire à un appelant que sa requête était malformée l'invite à cesser de réessayer une requête qui a toujours été bien formée. Un interrogateur ne devrait jamais avoir à lire de la prose pour distinguer l'absence de l'erreur, ou son propre bogue de celui du nœud.

Réorganisations#

La recherche par hauteur ne répond que pour la chaîne canonique. Un bloc qui perd une réorganisation garde son en-tête et perd son corps, si bien que son identifiant se résout encore, revient marqué orphaned, et porte encore le lien de parent jusqu'au point d'embranchement — ce qui fait de la recherche par identifiant le seul chemin sûr face aux réorganisations.

La joignabilité, et pourquoi elle compte plus qu'il n'y paraît#

Zycord ne fait pas de traversée de NAT. Un nœud avec --listen sur un port atteignable est un endroit depuis lequel d'autres peuvent s'amorcer, et la proportion de tels nœuds est la condition falsifiable sur laquelle repose toute la décision de ne pas traverser les NAT.

# periphery: nothing to configure
zycordd --testnet --dir ./testnet

# core: bind a port, and make sure it is actually reachable
zycordd --testnet --dir ./testnet \
  --listen 0.0.0.0:9421 --advertise <public-address>:9421
Annoncez une adresse qui répond, ou aucune du tout

--advertise se rabat sur --listen, si bien que --listen 0.0.0.0:9421 sans --advertise publie 0.0.0.0:9421, que personne ne peut appeler. Une adresse erronée se propage par l'échange de pairs et coûte à chaque nœud qui l'essaie un appel. Si vous ne pouvez pas rediriger de port, laissez --listen de côté et restez en périphérie.

Vérifier que cela a fonctionné#

curl -s localhost:9420/network
{"enabled":true,"peers":8,"listening":true,"inbound":5,"outbound":3,"reachable":true}

listening: true avec inbound: 0 après que le nœud tourne depuis quelques minutes signifie que le port n'est en fait pas atteignable : le processus est lié et attend, et rien n'arrive. Cela a l'air sain dans toutes les autres vues, et c'est pourquoi ce point d'entrée existe.

Adresses d'amorçage depuis un fichier#

zycordd --testnet --dir ./testnet --peers-file peers.txt

Une adresse par ligne ; les commentaires # et les lignes vides sont ignorés, et la liste est fusionnée avec les seeds intégrés du réseau. --peers prend la même chose sous forme d'argument séparé par des virgules, et --no-seeds écarte les seeds intégrés tout en conservant les deux.

L'interface du portefeuille, via un tunnel ssh#

Un nœud ne détient aucune clé et n'en détiendra jamais. Le portefeuille est un processus distinct, et sur un serveur c'est zcd ui :

zcd ui --key wallet.json --no-open

Cela imprime une URL et la sert sur 127.0.0.1:9430. Il se lie à la boucle locale et refuse toute autre chose, sans drapeau pour passer outre. Le processus derrière cet écouteur détient une clé privée déverrouillée et s'authentifie par un jeton porteur dans une URL, ce qui est adéquat pour une socket que seule la machine locale peut ouvrir et ne l'est pour rien d'autre. L'atteindre depuis ailleurs est le travail de ssh, et ssh y est meilleur que ceci ne le serait :

# on your own machine
ssh -L 9430:127.0.0.1:9430 <host>

Ouvrez ensuite l'URL que le serveur a imprimée. Le jeton voyage dans le fragment de l'URL, si bien qu'il n'atteint jamais le serveur, un journal, ni un en-tête Referer — traitez l'URL comme le secret qu'elle est. Elle est neuve à chaque exécution, et Ctrl-C efface la clé et arrête le service.

L'extrémité locale de la redirection n'a pas à être le port 9430 : l'interface vérifie le nom d'hôte dans l'en-tête Host et délibérément pas le port. Le nom d'hôte est ce qui compte — c'est lui qui bloque le rebinding DNS, qui est la véritable attaque contre un serveur sur la boucle locale, puisque n'importe quelle page de votre navigateur peut faire des requêtes vers 127.0.0.1 et que la seule chose qu'elle ne peut pas contrefaire est le nom vers lequel elle a été dirigée.

Ne placez pas de proxy inverse devant zcd ui

Le conseil ci-dessus sur le RPC du nœud porte sur une surface qui ne détient aucune clé et n'accorde aucune autorité. Celle-ci détient une clé. Il n'existe aucune version de son exposition qui soit une bonne idée, et le tunnel coûte un drapeau.

Deux autres formes qu'il vaut la peine de connaître :

  • zcd ui --locked démarre sans demander de phrase de passe dans le terminal ; c'est le navigateur qui la demande. Utile quand le terminal est partagé ou journalisé.
  • zcd ui --lock-after 5m raccourcit le verrouillage par inactivité, lequel efface la clé sur place — la graine est écrasée, et non simplement déréférencée.

Identité de pair et anonymat#

Le nœud génère une nouvelle clé de pair Ed25519 à chaque démarrage et ne l'écrit jamais sur disque. Elle n'est dérivée de rien — ni de votre portefeuille, ni d'une graine, ni de quoi que ce soit d'autre sur la machine. Redémarrer la fait tourner.

Si vous faites tourner un nœud d'une manière qui vous importe, ce qui vous désanonymise n'est pas la clé. C'est l'adresse. Un nœud d'amorçage que vous faites tourner sur une infrastructure traçable jusqu'à vous est un vecteur de désanonymisation qu'aucune hygiène de clés ne corrige.

Répertoire de données#

data/
  chain/       blocks, state, and the write-ahead log
  peers.json   the persisted peer store

Le magasin de pairs est persisté à dessein : un nœud qui repart d'une page blanche après chaque redémarrage offre à un attaquant une nouvelle occasion de le remplir. Le supprimer est sans danger mais jette cela.

Le nœud survit à être tué à tout moment — c'est à cela que sert le journal d'écriture anticipée, et c'est testé en tuant des nœuds au hasard sous un chaos réseau. Il ne survit pas à ce que son répertoire de données soit modifié sous lui : au démarrage, il recalcule la racine d'état et refuse de tourner si celle qui est stockée diverge.

Choisir un réseau#

DrapeauRéseauIdentifiant de chaîneMoteur
(aucun)mainnet1randomx-v1
--testnettestnet public2randomx-v1
--devnetdevnet local1337moteur de développement

Les paramètres sont embarqués dans le binaire, non lus depuis un chemin. Chaque participant d'un réseau public doit porter les mêmes octets, sans quoi ils ne sont pas sur un même réseau, et un fichier passé par --params est un fichier qui dérive. Un autre réseau est un autre réseau : les deux se déconnectent à la poignée de main, et un répertoire de données contenant la mauvaise chaîne est refusé au démarrage plutôt que mélangé en silence.

Réparer un répertoire de données endommagé#

zycordd embarque un chemin de réparation pour un répertoire de données contre lequel un nœud refuse de démarrer. Arrêtez d'abord le nœud — zycordd repair --dir ./data prend le verrou du répertoire et refuse tant qu'un nœud le détient — puis demandez ce qui s'est passé avec --dry-run avant de changer quoi que ce soit. Une seule classe de dommage se répare sur place ; pour les autres le remède est une resynchronisation, et c'est l'exécution à blanc qui vous dit laquelle vous avez.