État des lieux des différentes chaînes

Dans le cadre de “Industrialiser le démarrage d'une nouvelle Ğx”, j’ai dû faire le point sur les différentes chaînes à notre disposition dans le CLI Duniter V2S. Je vous propose un état des lieux (AVANT) puis ce que j’imagine comme fonctionnement (APRES).

Dans la partie APRES, j’introduis une séparation du Genesis Config en deux fichiers :

  • <currency>-config.json : fichier de configuration qui contient les quelques paramètres spécifiques de la monnaie (montant du DU, fréquence, …, WoT des autorités, session_keys)
  • g1-data.json : fichier qui contient la WoT Ğ1 + les portefeuilles, commun à toutes les monnaies
Chaîne Runtime Genesis (AVANT) Genesis (APRES)
dev ĞDev Basé sur le fichier DUNITER_GENESIS_CONFIG si fourni, autorité seule Alice. Sinon, auto-généré via 3 paramètres. Auto-généré via 3 paramètres.
gdev_local ĞDev Auto-généré via 3 paramètres. Dérivé de gdev-config.json* + g1-data.json, autorité seule Alice (ajoutée dynamiquement aux deux WoT).
gdev_live ĞDev Basé sur le fichier DUNITER_GENESIS_CONFIG si fourni, avec les autorités indiquées. Sinon, auto-généré via 3 paramètres. Dérivé de gdev-config.json* + g1-data.json, autorités de gdev.json.
gdev ĞDev Embarqué dans le binaire dédié. Embarqué dans le binaire dédié.
gdev-benchmark ĞDev Auto-généré. Auto-généré.
gtest_dev ĞTest Basé sur le fichier DUNITER_GENESIS_CONFIG si fourni, autorité seule Alice. Sinon, auto-généré via 3 paramètres. Dérivé de gtest-config.json* + g1-data.json, autorité seule Alice (ajoutée dynamiquement aux deux WoT).
gtest_live ĞTest Basé sur le fichier DUNITER_GTEST_GENESIS. Dérivé de gtest-config.json* + g1-data.json, autorités de g1.json.
gtest ĞTest Embarqué dans le binaire dédié. Embarqué dans le binaire dédié.
g1 Ğ1 Non impleménté. Décliner en g1_dev, g1_live et g1 comme pour les chaînes gtest.
/fichier.json Déduit du nom de fichier. Fourni à l’exécution (via fichier de specs). Fourni à l’exécution (via fichier de specs).

* : ou du fichier précisé par DUNITER_GENESIS_CONFIG si défini

L’idée est d’avoir une méthode de construction de Genesis commune pour les 3 monnaies, et que seul le fichier de configuration, court et présent dans le code source pour chaque monnaie, soit à spécifier : celui-ci doit être facilement consultable, vérifiable et modifiable.

Les données de la Ğ1 sont quant à elles déposées dans un fichier séparé par py-g1-migrator, afin de ne pas polluer la lecture du fichier de conf précédent.

Egalement, les seules chaînes pour lesquelles les données sont auto-générées sont la dev et gdev-benchmark. J’ai donc supprimé plusieurs cas où l’on avait des toiles auto-générées selon qu’une variable d’environnement était présente, car je ne trouvais pas ça clair.

Pour la chaîne benchmark, à voir s’il ne serait pas plus intéressant d’avoir au contraire les données de la Ğ1.

Voilà, si avez quelques retours à faire je suis preneur. Avant que je ne détricote le code. :slight_smile:

4 Likes

Super chantier de simplifier les chaînes ! J’avoue que ça m’avait causé pas mal de nœuds au cerveau de comprendre comment c’était fichu, notamment les variables d’environnement, je trouvais aussi ça vraiment pas clair. Pour l’instant je m’étais contenté d’ajouter beaucoup de commentaires dans le code et de lisser le comportement pour le runtime gtest.

En voyant ton tableau je me dis qu’on pourrait ajouter une feature de compilation pour activer certaines fonctionnalités sur demande à la compilation.

→ et en regardant le code je m’aperçois que je l’ai déjà fait pour la gtest :

#[cfg(all(feature = "gtest", feature = "embed"))]
"gtest" => Box::new(chain_spec::gtest::ChainSpec::from_json_bytes(
    &include_bytes!("../specs/gtest-raw.json")[..],
)?),

L’idée est que une fois qu’on est content d’un réseau live, on fige les raw chainspecs, et on compile avec la feature embed pour fournir un binaire “clé en main” aux utilisateurs. En résumé, ce que je pense qu’il faudrait faire :

runtime feature other feature gdev_live gdev gtest_live gtest
gdev :heavy_check_mark:
gdev embed :heavy_check_mark:
gtest :heavy_check_mark:
gtest embed :heavy_check_mark:

Cependant, pour ça, il faut s’assurer que le genesis est reproductible (pas de timestamp ou de nombre vraiment aléatoire). Parce que sinon on pourrait démarrer un live network et ne plus être en mesure de produire le même genesis avec la command build-specs.


Question, quand tu écris “Dérivé de gdev-config.json* + g1-data.json”, comment est-ce que tu indiques le chemin de ces fichiers ? Est-ce qu’ils sont fournis à la compilation ou à l’exécution ?

Ah oui, j’avais mal lu le code : les raw-specs sont déjà dans le livrable pour les chaînes gdev et gtest, je pensais qu’on les fournissais à l’exécution. J’ai modifié mon tableau en conséquence.

Soit, mais ça signifie que pour une release avec reboot de blockchain il faut re-livrer l’exécutable complet, et peut-être même faire une déclinaison du livrable par blockchain, donc avoir :

  • duniter-gdev-600, basé sur le Genesis GDev-600 et supportant le Runtime ĞDev 600 en natif
  • duniter-gdev-601, basé sur le Genesis GDev-600 et supportant le Runtime ĞDev 601 en natif
  • duniter-gtest-100, basé sur le Genesis GTest-100 et supportant le Runtime ĞTest 100 en natif
  • duniter-gtest-102, basé sur le Genesis GTest-100 et supportant le Runtime ĞTest 102 en natif
  • duniter-g1-100, basé sur le Genesis G1-100 et supportant le Runtime Ğ1 100 en natif

Je suis parti du principe que les chaînes qui requièrent ces fichiers sont exécutées depuis le code source car exploitées par un développeur (donc compilation), tandis qu’un utilisateur exploiterait les chaînes gdev, gtest et g1 (donc exécution).

Cependant je garde la possibilité de préciser gdev-config.json (et équivalents pour gtest et g1) via la variable d’environnement DUNITER_GENESIS_CONFIG notamment pour les besoins de tests end2end et de CI (build des raw-specs à partir d’un dump “frais” de la Ğ1) qui peuvent vouloir préciser un autre fichier.

On peut les fournir à l’exécution, avec l’option --raw-spec, cf notre discussion Quel genesis une nouvelle version du client doit-il incorporer?. Ça permet de remplacer les raw specs incorporées par une version externe sans changer de binaire.

Oui, l’intérêt d’intégrer le genesis au binaire est qu’on n’a pas à la fournir en dehors. L’inconvénient est que pour changer de genesis, il faut livrer un nouveau binaire (ou utilise --raw-spec). Idem pour les bootnodes.

Ça dépend. Ce n’est pas immédiat d’établir si une version du client est compatible avec un runtime différent. En général ça fonctionne, d’où la possibilité de faire des runtime upgrade sans changer de client, mais si un runtime requiert des api client non présentes dans le client, ça peut ne pas fonctionner. Le versionnage du runtime est indépendant de celui du client. Il me semble que chez purestake, @elois avait travaillé à déterminer si un runtime et un client était compatibles (apparemment il travaille chez moonsong maintenant, d’ailleurs).

Donc en gros, si on reboot une blockchain, on commence par démarrer avec une chaîne “live”, puis on fait build-spec pour passer les raw chainspecs aux autres premiers forgerons. Ces derniers démarrent leur noeud avec un client compatible et l’option --raw-spec pour utiliser les specs du réseau. Enfin, une fois qu’on a un ensemble de bootnodes stables et de confiance, on les inclut dans des client specs et on publie un binaire “tout en un” pour les forgerons qui veulent juste lancer leur nœud mais pas configurer de chaîne custom et de bootnodes.

Ce n’est pas ça que je voulais dire, je reprends un de mes exemples :

Cette nomenclature permet de dire que :

  • le binaire embarque les raw-specs de la ĞDev (c’est la base du raisonnement, si l’on retire les options --chain=gdev, --chain=gtest et --chain=g1 alors tu peux ignorer ce post)
  • 601 permet de dire qu’il s’agit de la ĞDev dans sa série 600, mais n’est pas compatible avec une ĞDev basé sur un Genesis avec Runtime 500 à son lancement
  • et plus précisément le binaire est compilé avec le Runtime 601, ce qui rend possible l’exécution native du Runtime 601 plutôt que Wasm
1 Like

Ok, ce que je n’avais pas compris, c’est la différence entre l’exécution native et wasm du runtime. Je n’ai pas exploré la différence entre les deux, est-ce qu’il y a une histoire de performances par exemple ? Dans ma tête on embarquait toujours le runtime du genesis par simplicité d’utilisation, mais effectivement, on peut aussi embarquer le runtime actuel du réseau et donner le runtime du genesis avec l’option --raw-spec. Mais je ne connais le comportement du substrate dans ce cas, comment fait-il pour choisir le runtime natif plutôt que le wasm ?

Oui c’est juste une histoire de performances.

Et dans tous les cas on est obligés d’avoir un Genesis pour démarrer sur une blockchain Substrate et il y a forcément le Runtime initial dedans (c’est imposé par SystemConfig) pour interpréter le bloc#0 et les suivants jusqu’à un éventuel Runtime Upgrade.

Déjà il faut avoir activé l’option (--native-else-wasm par exemple).

Ensuite je ne sais pas trop, mais mes points d’arrêts m’avaient amené à cette instruction dans Substrate. En fait c’est en regardant la version du Runtime natif (ligne juste au-dessus) :

let can_call_with = 
  onchain_version.can_call_with(&self.native_version.runtime_version);

edit : ce n’est pas évident non plus ce block#0, apparemment celui-ci n’est pas importé mais – je pense – est déduit du Genesis. C’est vérifiable en lançant une monnaie locale temporaire sans validateur : on a bien un :

📦 Highest known block at #0
1 Like

C’est bien ça. D’ailleurs les logs au démarrage du client le disent :

🔨 Initializing Genesis block/state (state: 0x0a78…3600, header-hash: 0xc155…d186) 
...
📦 Highest known block at #0
...
💤 Idle (0 peers), best: #0 (0xc155…d186), finalized #0 (0xc155…d186), ⬇ 0 ⬆ 0

“Genesis block/state” : le Genesis désigne aussi bien le block#0 que l’état initial du Storage (au sens où le bloc#0 ne semble rien pouvoir contenir d’autre que l’état initial).

Je me demande d’ailleurs comment réagit la pallet timestamp à ce sujet puisqu’un inherent est attendu à chaque bloc.

edit : c’est tout bête : effectivement le bloc#0 n’est pas importé, c’est plutôt une étape de construction du Storage. Donc il n’est pas régit par les règles habituelles d’exécution du Runtime.

A post was split to a new topic: Exemple de bug lié à la différence entre runtime wasm et natif

A post was split to a new topic: Disparition du paramètre msPeriod (délai entre 2 renouvellements d’adhésion)