Dex (Duniter DBs Explorer)

Cet été je me suis amusé à développer un utilitaire Rust en ligne de commande: un explorateur de la DB de Duniter !

:warning: :warning: :warning:
ATTENTION : Je vous déconseille très fortement de fournir un logiciel où un service basé sur la DB de Duniter directement, car tout changement dans la structure de la DB de Duniter pourra casser votre service où logiciel.
La structure de la base de donnée va notamment être entièrement refondue dans une prochaine version de Duniter, donc si vous utiliser dex pour de l’export automatisé votre code ne fonctionnera plus (vous devrez le réécrire conformément à la nouvelle structuration des données).
De plus, sachez que seuls les besoins de Duniter seront considérés dans la conception des futures versions de la base de donnée, les besoins spécifique à votre services ne seront pas considérés.
Si vous avez besoin qu’une continuité soit assurée, vous devez utiliser l’api GVA (en cours de développement).
:warning: :warning::warning:

Pourquoi Dex ?

L’idée m’est venue en utilisant SQLite Browser pour débuger Duniter. Je me suis dit que ce serait bien d’avoir l’équivalent pour leveldb (et pour sled à venir).
L’objectif premier est donc de débuger, ça peut avoir d’autres usages comme de l’export régulier de données à des fin de statistiques où à toute autre fin mais vous devez être bien conscient des risques avant de choisir d’utiliser dex où non (cf intro).
Si quelqu’un veut ajouter le support du CSV je peux l’y aider :slight_smile:

Comment compiler Dex

Prerequis

Vous aurez besoin d’une version récente de cmake ainsi que des “buildessential tools”.
Si vous êtes sur debian/ubuntu où dérivé : apt install build-essential cmake

git clone https://git.duniter.org/nodes/typescript/duniter.git
cd duniter
cargo bdex

Le binaire exécutable se trouve alors ici : target/release/dex.

Et le script d’autocomplétion là : target/release/dex.bash

zsh et fish sont également supportés, voir le README :

https://git.duniter.org/nodes/typescript/duniter/-/blob/feat/dex/rust-bins/duniter-dbex/README.md

Que permet Dex ?

Dex permet d’explorer les données brutes présentes dans la base de données de Duniter, de les filtrer avec des regex et autres options avancées, et de les exportées au format Json ou dans un tableau textuel.

Par exemple je peux lister tous les portefeuilles ayant au moins 20000 Ğ1 :

$ ./dex find wallet -o table-properties -p balance -v "balance\":[2-9][0-9]{6,}"
Database opened in 0.145758 seconds.
Search performed in 0.159494 seconds.

18 entries found.
+---------------------------------------------------+---------+
| Key                                               | balance |
+=============================================================+
| SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ) | 3256534 |
|---------------------------------------------------+---------|
| SIG(2sZF6j2PkxBDNAqUde7Dgo5x3crkerZpQ4rBqqJGn8QT) | 2082576 |
|---------------------------------------------------+---------|
| SIG(4cAkMCd1N7gjz2JTA3ac5bxSCkgxUgWjpEmKkySCGRzQ) | 6927337 |
|---------------------------------------------------+---------|
| SIG(Cy49SCD2S9x6KFc8Jf9hypAXHn2SiSKF5B3xsdSgeXt2) | 2009555 |
|---------------------------------------------------+---------|
| SIG(D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx) | 2376030 |
|---------------------------------------------------+---------|
| SIG(F5cap3UX1yF1yQaA9rxCzYK9q7EMf3dwX4d6hWYp3Fh2) | 2000000 |
|---------------------------------------------------+---------|
| SIG(HjqcogJRhianwxihURoY4hpBuGm4W9voqD3dkxaWwzva) | 3147879 |
|---------------------------------------------------+---------|
| SIG(RML12butzV3xZmkWnNAmRwuepKPYvzQ4euHwhHhzvSY)  | 3536832 |
|---------------------------------------------------+---------|
| SIG(TENGx7WtzFsTXwnbrPEvb6odX2WnqYcnnrjiiLvp1mS)  | 2134988 |
|---------------------------------------------------+---------|
| SIG(c9t2jXgpPps9g32zmbEW9kYFdenGgoADC9nQrSTpZne)  | 2223183 |
|---------------------------------------------------+---------|
| SIG(38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE) | 2371468 |
|---------------------------------------------------+---------|
| SIG(3zqbvxd6sDMbyHwpik56Hmsz1WUm9uiyNJnrHubzh152) | 5843396 |
|---------------------------------------------------+---------|
| SIG(4FgeWzpWDQ2Vp38wJa2PfShLLKXyFGRLwAHA44koEhQj) | 3959018 |
|---------------------------------------------------+---------|
| SIG(78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8) | 9165199 |
|---------------------------------------------------+---------|
| SIG(CHosFQox9qTSBkPyLSVupRiHUcXawwTodYEU6iqkcZs3) | 2053529 |
|---------------------------------------------------+---------|
| SIG(GUpwrxqsYVBJru9orMteswESNoLAREzrK7xM75w42GD5) | 2080148 |
|---------------------------------------------------+---------|
| SIG(GfKERHnJTYzKhKUma5h1uWhetbA8yHKymhVH2raf2aCP) | 3035709 |
|---------------------------------------------------+---------|
| SIG(HHu6NZnpSvWtkGNnksz7ud2UtMYh7xM8KhL8JcvLvNJK) | 2071656 |
+---------------------------------------------------+---------+
Search results were displayed in 0.000842 seconds.

Comment l’utiliser

Il est nécessaire d’interrompre votre nœud Duniter afin de déverrouiller la base de données :

duniter stop
dex find <options>
duniter webstart

Vous pouvez également faire une copie de votre DB afin de pouvoir l’explorer tranquillement, attention dans ce cas il faut indiquer à dex où aller chercher la copie :

duniter stop
cp -r ~/.config/duniter/duniter_default/data ~/.config/duniter/copy1/data 
duniter webstart
dex -p copy1 find <options>

Vous pouvez évidemment remplacer copy1 par le nom de votre choix :wink:

Pour les différentes commandes et options, faites dex --help

Pourquoi DBs au pluriel ?

Parce que je prévois qu’il y ait plusieurs DB leveldb (puis sled) dans le futur, notamment une pour la mempool et une pour les fiches de peer(afin de supprimer définitivement SQLite) et peut-être une pour GVA.
Dex permettra d’explorer toutes les futures DBs de Duniter, je j’ai codé de façon a ce que l’ajout d’une DB demande très peu de code coté Dex.

6 Likes

Je viens de rajouter un Disclaimer très important :

:warning: :warning: :warning:
ATTENTION : Je vous déconseille très fortement de fournir un logiciel où un service basé sur la DB de Duniter directement, car tout changement dans la structure de la DB de Duniter pourra casser votre service où logiciel.
La structure de la base de donnée va notamment être entièrement refondue dans une prochaine version de Duniter, donc si vous utiliser dex pour de l’export automatisé votre code ne fonctionnera plus (vous devrez le réécrire conformément à la nouvelle structuration des données).
De plus, sachez que seuls les besoins de Duniter seront considérés dans la conception des futures versions de la base de donnée, les besoins spécifique à votre services ne seront pas considérés.
Si vous avez besoin qu’une continuité soit assurée, vous devez utiliser l’api GVA (en cours de développement).
:warning: :warning::warning:

1 Like
poka@pokaMint-desktop:~/dev/duniter$ git branch
  dev
* release/1.8
poka@pokaMint-desktop:~/dev/duniter$ git remote -v
origin  https://git.duniter.org/nodes/typescript/duniter.git (fetch)
origin  https://git.duniter.org/nodes/typescript/duniter.git (push)
poka@pokaMint-desktop:~/dev/duniter$ git status
Sur la branche release/1.8
Votre branche est à jour avec 'origin/release/1.8'.

rien à valider, la copie de travail est propre
poka@pokaMint-desktop:~/dev/duniter$ cargo bdex
error: no such subcommand: `bdex`

        Did you mean `add`?

dex n’existe pas sur la branche release/1.8, il faut utiliser la branche dev

1 Like

C’est bon j’ai pu récupérer un build depuis une VM que j’avais fait, j’ai pu faire dex export-bc avec succès, mais c’est pas les chunks que je veux, je vais regarder comment ça se passe.


@elois le résultat que sort yes |./dex find mb_certs, colonne de droite ce sont les certs émisent ou reçus par pubkey du coup ?

Seconde question, je n’arrive pas à rediriger la sortie de dex dans un fichier à cause de la validation yes nécessaire, y a il un moyen de dump la sortie dans un fichier simplement, et peut-on l’avoir en format JSON ?
(je n’arrive même pas à voir le haut du résultat pour voir l’intitulé de la colonne avec |head justement, en bidouillant un peu avec |more j’arrive à voir que les intitulés sont juste key, value…)

Je ne peux pas renseigner de path pour dump comme la commande dex export-bc export-bc/ le propose.


edit: D’après les données que j’observe ce sont les certs reçus, donc c’est nickel!
Bon ce sont des BlockNumberArrayV1 en fait, pas des numéros d’identités.

De toute façon je comprends qu’il faut ajouter une commande à Dex pour sortir les données qu’on veut au format qu’on veut, avec les dates de certifs et les soldes.

Si tu avais fait dex find -h tu verrais qu’il a déjà tout ce qu’il faut dans dex pour exporter dans un fichier dans différents format dont le JSON…

1 Like

Je pensais que -h donnais la même chose que juste lancer la sous-commande sans arguments, mais non, merci, c’est bon ça marche.

1 Like

Non, car chaque sous commande à ses propres options, donc il faut demander l’aide pour chaque sous commande, y compris les sous-sous commandes s’il y a lieu (et toutes les profondeurs en fait).

@poka aussi pour comprendre le format des données:

Chaque table est une collection de clés valeurs.
Pour comprendre le format d’une table, le mieux est d’explorer quelques valeurs avec les options --limit et --pretty.

Exemple pour comprendre la table cindex:

$ ./dex -p copy1 find cindex -l 1 --pretty
Database opened in 0.079625 seconds.
Search performed in 0.000333 seconds.

1 entries found.
+-----------------------------------+-----------------------------------+
| Key                               | Value                             |
+=======================================================================+
| 127i8bbhkbr6qzpbCyrhiveKR5d9AwPD4 | {                                 |
| uoxyKM5kPDQ                       |   "issued": [                     |
|                                   |     {                             |
|                                   |       "age": 1692,                |
|                                   |       "chainable_on": 1645522414, |
|                                   |       "created_on": 501273,       |
|                                   |       "created_on_ref": null,     |
|                                   |       "expired_on": 0,            |
|                                   |       "expires_on": 1708203695,   |
|                                   |       "fromMember": null,         |
|                                   |       "isReplay": null,           |
|                                   |       "isReplayable": null,       |
|                                   | "issuer": "127i8bbhkbr6qzpbCyrhiv |
|                                   | eKR5d9AwPD4uoxyKM5kPDQ",          |
|                                   |       "op": "CREATE",             |
|                                   | "receiver": "AghoLFNVqTdAJVvKtuZg |
|                                   | QcPihNAZNEKABNGpft7cZShG",        |
|                                   | "replayable_on": 1650350014,      |
|                                   | "sig": "UZm+rUGYAjxjnOgNYoGp7kDDc |
|                                   | xIHXAnxBSU2Wy5/ck1GyTHpbJeYtl7f3U |
|                                   | fvnBKyGL5Jz8c9RSO293nOwZvNDg==",  |
|                                   |       "sigOK": null,              |
|                                   |       "stock": 100,               |
|                                   |       "toLeaver": null,           |
|                                   |       "toMember": null,           |
|                                   |       "toNewcomer": null,         |
|                                   |       "unchainables": 0,          |
|                                   |       "writtenOn": 501281,        |
|                                   | "written_on": "501281-0000002F99D |
|                                   | A8E26FD736D65A32240787A9B7EE76902 |
|                                   | 3574E79A89F023B9A7AB"             |
|                                   |     },                            |
|                                   |     {                             |
|                                   |       "age": 1866,                |
|                                   |       "chainable_on": 1647241864, |
|                                   |       "created_on": 506777,       |
|                                   |       "created_on_ref": null,     |
|                                   |       "expired_on": 0,            |
|                                   |       "expires_on": 1709922904,   |
|                                   |       "fromMember": null,         |
|                                   |       "isReplay": null,           |
|                                   |       "isReplayable": null,       |
|                                   | "issuer": "127i8bbhkbr6qzpbCyrhiv |
|                                   | eKR5d9AwPD4uoxyKM5kPDQ",          |
|                                   |       "op": "CREATE",             |
|                                   | "receiver": "6FcBCA8ZYwRLcSBvDyaK |
|                                   | wMvU1m5n7sLiH2Eaj8ua4vom",        |
|                                   | "replayable_on": 1652069464,      |
|                                   | "sig": "FvRjPKBuZSsQP7sRKdq6mJmA5 |
|                                   | pc+MOzXLHxnjqHf3TuuFXPA0rfrqVxE16 |
|                                   | PNaUOQM/5h5T6YFeKi768a57URBA==",  |
|                                   |       "sigOK": null,              |
|                                   |       "stock": 100,               |
|                                   |       "toLeaver": null,           |
|                                   |       "toMember": null,           |
|                                   |       "toNewcomer": null,         |
|                                   |       "unchainables": 0,          |
|                                   |       "writtenOn": 506785,        |
|                                   | "written_on": "506785-00000025B88 |
|                                   | 344BC499DB65EA9C42540587071790254 |
|                                   | 65F331FAD58B05E35694"             |
|                                   |     }                             |
|                                   |   ],                              |
|                                   |   "received": [                   |
|                                   | "AghoLFNVqTdAJVvKtuZgQcPihNAZNEKA |
|                                   | BNGpft7cZShG",                    |
|                                   | "BX5zbBTQFdwpW3xnas8RodqgHo1hDjye |
|                                   | RD8vUPVkUCYZ",                    |
|                                   | "35Xjn9ZWEkPnuP2WpFanQUhp7n253wsy |
|                                   | mRZxTTi9VNUW",                    |
|                                   | "3mafDaEbdu9MSF9We6x3aXPLKQscW4ct |
|                                   | JeVEcAN4574w",                    |
|                                   | "BzsypG3YLxGnEDixa6arBgNUZ9ga6UuA |
|                                   | adQp56QpAxiQ",                    |
|                                   | "6FcBCA8ZYwRLcSBvDyaKwMvU1m5n7sLi |
|                                   | H2Eaj8ua4vom"                     |
|                                   |   ]                               |
|                                   | }                                 |
+-----------------------------------+-----------------------------------+
Search results were displayed in 0.000642 seconds.
1 Like

Arf je vois qu’il y a la détails des certifications envoyés, avec date et tout nickel, mais par contre pour les received, ce n’est qu’une liste de pubkey :confused:

Je suppose que ça doit être assez simple de juste reprendre le même format que pour issuer, je vais essayer de voir dans le code.

Toute certification envoyée est aussi une certification reçue, si tu extrais le json complet de toute la table cindex tu as toutes les données nécessaires pour tout savoir sur toutes les certifications.
Du moment que tu as compris le format, c’est juste de la manipulation de données que tu peux faire en python ou n’importe-quel autre langage.

1 Like

C’est très vrai, c’est le réflex que j’ai eu en voyant ça, mais je suis juste déçus de pas avoir les bonnes données toutes cuites avec les receiver, maintenant que tu me dis ça fallait pas me le dire 2 fois, je scrap ça, déduis les numéros de bloc relatifs à partir de genesis_certs_expire_on, j’ajoute les soldes et voilà ^^

1 Like

dex ne fait aucune manipulation de données, il te sert les données tel qu’elles sont structurées dans la db leveldb conçue par cgeek et utilisée actuellement en production par duniter-v1, dex est uniquement un outil d’exploration et d’export :slight_smile:

1 Like