Migration de l’historique des transfers pour la migration en v2?

Les données migrées de la v1 à la v2 concernant l’aspect monétaire sont uniquement les soldes.
Dans l’écosystème v2, la manière de récupérer l’historique des transferts pour les clients est uniquement via les indexeurs et non via RPC. Est-ce correct ?

Est-il possible d’insérer les transferts dans le bloc #0 ou une autre BDD de Duniter v2s ?
Si non, les insérer (“harcoder” une BDD historique transferts v1) dans les indexeurs me semble caduc.

Il faudrait migrer l’historique des transferts (avec condition de déverrouillage simple avec signature SIG()) en convertissant uniquement les non-UTXO du transfert, car des derniers n’apportent rien dans l’écosystème v2 (qui est un mécanisme de fonctionnement de la v1) :
A envoie 10 à B, il casse une source de 20 et renvoie 10 à lui-même.

  • A: 10 −> B: 10 (on migre cette information)
  • A: 10 −> A: 10 (UTXO : information non migrée)

Qu’en pensez-vous ? Est-ce que ça vaut la peine, si possible techniquement, qu’on se donne la peine de migrer l’historique des transferts qui ont eu lieu dans l’ère de la v1 pour pouvoir les afficher avec les clients de l’écosystème v2 ? Une fois la migration faite, aller consulter l’historique avec un client v1 sur une instance Duniter v1, ça fait un peu rédhibitoire. Cela dit, consulter l’historique v1 un an ou deux ans après la migration ne ferait peut-être plus très sens.

1 Like

On pourrait faire vivre une blockchain substrate de manière totalement artificielle avec un bloc toutes les ~ 5 minutes dans le passé et un timestamp_set correspondant au median_time des blocs v1, puis un runtime upgrade qui retire le sealing manuel et passe à une blockchain “live”. J’y ai pensé au tout début parce que ce serait très satisfaisant d’un point de vue conceptuel, mais vite abandonné l’idée parce que bien trop complexe à réaliser, ça n’en vaut pas la peine à mon avis.

Oui, ça vaut la peine, est c’est précisément ce qu’on fait au niveau de l’indexeur duniter-squid, comme tu le dis (oui, c’est correct) :

Mais ça ne doit bien sûr pas être la seule manière, il faudra conserver une archive exhaustive de la blockchain (au format json par exemple, comme je le fais ) pour pouvoir retransformer à volonté de manière statique les données source dans le format qui nous conviendra, que ce soit dans les indexeurs, ou même une API dédiée semblable à BMA servie par une sous-partie de Duniter.

Pour Gexplore j’avais imaginé faire deux backends DUBP et Substrate respectant chacun leurs propres numéros de blocs, et un troisième capable de chaîner les deux, en adaptant les données. L’indexeur utiliserait ce dernier backend.

Le but de Gexplore est justement d’explorer l’historique donc ce serait dommage d’oublier des années d’états de la TdC.

Ça oblige soit à rendre très génériques les données indexées, soit à adapter les données v1, soit à indexer deux formats différents.

Du point de vue d’un tel indexeur, si on veut que le bloc 0 de la v2 soit bien un bloc 0, il faut trouver une convention pour numéroter les blocs v1, idéalement qui conserve l’ordre lexicographique et l’ordre numérique et qui soit transparente (sans besoin de calculer pour retrouver le numéro v1). Je n’ai pas de solution vraiment satisfaisante (par exemple pour nommer le bloc 123 de la v1 : 100000000123, dubp123, --123). La notation avec préfixe explicite semble plus pérenne (et si on change à nouveau de blockchain dans 10 ans ?).

1 Like

J’ai pas mal avancé dessus et repris py-g1-migrator et le genesis duniter-squid. Voici des extraits de code pour ce qui touche aux transactions (les certifications c’est un peu plus compliqué) :

:link: lib/functions.py · ce8a73c7844cb04814246039dcc325d4a026239b · tools / py-g1-migrator · GitLab

def get_tx(leveldb_path: str) -> list:
    """
    Get tx,
    return a list of tx
    """
    # Get wallets balances data
    blocks_repo = LevelDBBlocksRepository(leveldb_path)
    txs = []
    for num, block in blocks_repo:
        if num % NOTIF_INTERVAL == 0:
            print(num)
        for tx in block.get("transactions"):
            outputs = tx["outputs"]
            issuers = tx["issuers"]
            comment = tx["comment"]
            timestamp = block["medianTime"]
            issuers_count = len(issuers)
            # loop on issuers. If multiple issuers, approximate amount
            for issuer in issuers:
                # loop on outputs
                for output in outputs:
                    outputparts = output.split(":")
                    amount = int(outputparts[0])
                    receiver = outputparts[2]
                    if issuers_count > 1:
                        amount = math.floor(amount / issuers_count)  # approximation
                    # ignore non trivial unlock sources
                    # https://git.duniter.org/tools/py-g1-migrator/-/issues/3
                    if "&&" in receiver or "||" in receiver:
                        print(num)
                        print("ignoring " + receiver)
                        continue
                    receiver = receiver.split("SIG(")[1].split(")")[0]
                    sample = {
                        "blockNumber": num,
                        "timestamp": timestamp,
                        "from": issuer,
                        "to": receiver,
                        "amount": amount,
                        "comment": comment,
                    }
                    # do not include outputs that go back to sender
                    if sample["from"] != sample["to"]:
                        txs.append(sample)
    return txs

:link: src/genesis/genesis.ts · 7864398a48e981b53b8af5e709a6605b424998e7 · nodes / duniter-squid · GitLab

// add txs
let genesis_tx_counter = 0;
for (const tx of tx_history) {
  // only older blocks
  if (tx.blockNumber > last_v1_block) continue
  // height conversion
  const negHeight = v1_to_v2(tx.blockNumber)
  // process
  genesis_tx_counter += 1;
  const date = new Date(tx.timestamp * 1000); // seconds to milliseconds
  const from = accounts.get(safePubkeyToAddress(tx.from));
  const to = accounts.get(safePubkeyToAddress(tx.to));
  genesis_transfers.push(
    new Transfer({
      id: `genesis-tx_n°${genesis_tx_counter}`,
      blockNumber: negHeight,
      timestamp: date,
      from,
      to,
      amount: BigInt(tx.amount),
      comment: tx.comment,
    })
  );
}

Les fichiers json seront fournis en assets GitLab sur la release.

Exemple de CI : create_g1_data (#131675) · Jobs · nodes / rust / Duniter v2S · GitLab

$ rm g1-dump.tgz
$ mv tmp/backup-g1-duniter-1.8.7 duniter_default
$ git clone https://git.duniter.org/tools/py-g1-migrator.git --depth 1 --branch hugo/docker /py-g1-migrator
Cloning into '/py-g1-migrator'...
$ cd /py-g1-migrator
$ ./main.py
Generate ĞTest genesis with up-to-date Ğ1 data
    dump ĞTest parameters...
    parse Identities...
    parse Wallets...
⚠️ wallet SIG(38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE) && SIG(GfKERHnJTYzKhKUma5h1uWhetbA8yHKymhVH2raf2aCP) ignored (balance 1002)
⚠️ wallet SIG(Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P) && SIG(7F6oyFQywURCACWZZGtG97Girh9EL1kg2WBwftEZxDoJ) ignored (balance 103)
⚠️ initial monetary mass 9,173,584,257 does not equal wallet sum 9,173,554,151
money on the wallets: 9,173,553,046
money from ignored sources: 1,105
missing money (added to treasury): 30,106
    parse certifications...
    add simple wallets...
Done
$ ./squid-block.py
Prepare blocks for squid
0
100000
200000
300000
400000
500000
600000
700000
Exporting...
Done
$ ./squid-cert.py
Prepare cert for squid
0
100000
200000
300000
400000
500000
600000
700000
Exporting...
Done
$ ./squid-tx.py
Prepare tx for squid
0
100000
200000
300000
318637
ignoring SIG(CvrMiUhAJpNyX5sdAyZqPE6yEFfSsf6j9EpMmeKvMCWW) || (SIG(CmFKubyqbmJWbhyH2eEPVSSs4H4NeXGDfrETzEnRFtPd) && CSV(604800))
318637
ignoring SIG(CvrMiUhAJpNyX5sdAyZqPE6yEFfSsf6j9EpMmeKvMCWW) || (SIG(CmFKubyqbmJWbhyH2eEPVSSs4H4NeXGDfrETzEnRFtPd) && CSV(604800))
365771
ignoring SIG(38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE) && SIG(GfKERHnJTYzKhKUma5h1uWhetbA8yHKymhVH2raf2aCP)
400000
409573
ignoring SIG(Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P) && SIG(7F6oyFQywURCACWZZGtG97Girh9EL1kg2WBwftEZxDoJ)
500000
600000
700000
Exporting...
Done
$ mkdir -p $CI_PROJECT_DIR/release/
$ cp output/genesis.json $CI_PROJECT_DIR/release/
$ cp output/block_hist.json $CI_PROJECT_DIR/release/
$ cp output/cert_hist.json $CI_PROJECT_DIR/release/
$ cp output/tx_hist.json $CI_PROJECT_DIR/release/
Uploading artifacts for successful job 00:12
Uploading artifacts...
/builds/nodes/rust/duniter-v2s/release/: found 5 matching artifact files and directories 
g artifact files and directories 
Uploading artifacts as "archive" to coordinator... 201 Created  id=131675 responseStatus=201 Created token=glcbt-64
Cleaning up project directory and file based variables 00:01
Job succeeded

Et les artefacts : release · Artifacts · create_g1_data (#131675) · Jobs · nodes / rust / Duniter v2S · GitLab