Lire les données brutes d’une blockchain · Leçon 6 sur 6
Qualité des données, normalisation et provenance
10 min de lecture
La plupart des chiffres erronés dans l’analyse de blockchain ne proviennent pas de mauvaises heuristiques, mais d’erreurs de données ordinaires : un montant décalé d’un facteur de 10^12, une limite de journée dans le mauvais fuseau horaire, un transfert compté deux fois, une transaction Polygon mélangée aux totaux d’Ethereum. Ces erreurs sont faciles à commettre et difficiles à repérer dans un graphique finalisé. Cette leçon présente les règles de normalisation qui permettent de les éviter, ainsi que la trace de provenance qui permet à quiconque de vérifier votre travail.
Unités : conserver les entiers jusqu’à la fin
Les blockchains stockent les montants sous forme d’entiers exprimés dans la plus petite unité. Les virgules décimales sont une convention d’affichage.
| Actif | Unité de base | Conversion |
|---|---|---|
| Bitcoin | satoshi | 1 BTC = 100 000 000 sat (10^8) |
| Ether | wei | 1 ETH = 10^18 wei ; 1 gwei = 10^9 wei |
| Jetons ERC-20 | unités brutes | Diviser par 10^decimals, valeur lue dans le contrat du jeton |
Le nombre de décimales des jetons varie : 18 est courant, mais USD Coin en utilise 6 et Wrapped Bitcoin en utilise 8. La valeur decimals est facultative dans la norme ERC-20 et purement informative : les soldes du contrat sont toujours des entiers bruts. Lisez-la dans chaque contrat sur chaque chaîne plutôt que de faire des suppositions, car une version pontée d’un jeton peut différer de l’original. Une supposition erronée peut avoir de lourdes conséquences : un montant brut de 2 500 000 000 représente 2 500 jetons à 6 décimales, mais 0,0000000025 à 18 décimales, soit un facteur de 10^12.
Deux pièges sont à éviter. Premièrement, la virgule flottante. Bitcoin Core renvoie les montants en BTC sous forme de nombres JSON, tels que 0.00485900, et un analyseur imprudent les convertit en valeurs binaires à virgule flottante, qui ne peuvent pas représenter exactement la plupart des fractions décimales. Les sommes dérivent alors de petites quantités et les rapprochements échouent. Analysez les montants comme des chaînes décimales et convertissez-les immédiatement en satoshis entiers. Deuxièmement, le dépassement de capacité. Les montants Ethereum sont des entiers non signés de 256 bits, pouvant atteindre environ 1,16 × 10^77. Un BIGINT signé de 64 bits plafonne à 9 223 372 036 854 775 807, ce qui ne représente qu’environ 9,22 ETH en wei ; même un DECIMAL à 38 chiffres ne peut pas contenir toutes les valeurs possibles. Stockez les montants bruts sous forme de texte ou dans un type numérique de grande capacité, et convertissez-les à la toute dernière étape :
-- Les montants bruts restent exacts ; la conversion n’a lieu qu’à l’affichage
SELECT tt.tx_hash,
tt.log_index,
tt.raw_amount, -- entier exact conservé sous forme de texte
CAST(tt.raw_amount AS DECIMAL(38, 0))
/ POWER(10, tk.decimals) AS amount_display -- approximatif
FROM token_transfers tt
JOIN tokens tk
ON tk.chain_id = tt.chain_id
AND tk.contract_address = tt.contract_address;
La jointure sur chain_id et l’adresse du contrat sélectionne la bonne valeur decimals pour chaque jeton sur chaque chaîne. La conversion échoue pour les valeurs dépassant 38 chiffres, ce qui signale les jetons indésirables aux réserves absurdes au lieu de corrompre silencieusement les totaux ; certains moteurs proposent plutôt des décimaux à 76 chiffres ou des types natifs de 256 bits. POWER renvoie un nombre à virgule flottante dans la plupart des moteurs : amount_display est donc une approximation, acceptable uniquement pour l’affichage ; les sommes et les rapprochements utilisent raw_amount. Ne divisez pas plutôt par un DECIMAL(38, 0) : certains moteurs attribuent au résultat l’échelle des opérandes, qui est nulle ici, et tronquent silencieusement le résultat à un nombre entier.
Heure : UTC et heure des blocs
Les horodatages des blocs sont des horodatages Unix : le nombre de secondes écoulées depuis le 1er janvier 1970, en UTC. Conservez tous les horodatages en UTC, regroupez les jours et les mois en UTC et indiquez-le dans le rapport. La conversion vers un fuseau horaire local déplace des transactions d’un jour à l’autre, et les changements d’heure saisonniers créent des heures qui se produisent deux fois ou pas du tout. Une transaction minée à 23:30 UTC le 31 mars appartient au mois de mars dans un rapport UTC, mais au 1er avril pour un analyste dont les outils affichent silencieusement UTC+2 ; si deux équipes comparent leurs totaux mensuels ainsi calculés, elles obtiendront des résultats différents sans que l’une ou l’autre comprenne pourquoi. Stockez les horodatages en valeurs UTC (ou en secondes Unix brutes) et ne les convertissez qu’au niveau de la présentation, en indiquant le fuseau utilisé.
N’oubliez pas ce que signifie l’horodatage. Il s’agit de l’heure du bloc, et non du moment de la signature ou de la diffusion de la transaction ; sur Bitcoin, il peut même être antérieur à celui du bloc précédent. Ordonnez les événements par hauteur (et par position dans le bloc), et non par horodatage. Lorsque vous associez des activités entre chaînes, tenez compte des différents intervalles entre blocs et des règles d’horodatage.
Identifiants et leurs formats
Un même identifiant peut s’écrire de plusieurs façons, et les jointures échouent silencieusement lorsque les formats diffèrent.
- Les adresses EVM apparaissent sous forme de somme de contrôle en casse mixte EIP-55 (
0xA1b2…9F) dans les portefeuilles et les explorateurs, mais en minuscules dans les journaux et la plupart des jeux de données. Convertissez toutes les adresses et tous les hachages hexadécimaux en minuscules pour le stockage et les jointures ; ne conservez la forme avec somme de contrôle que pour l’affichage. - Valeurs complétées : une adresse dans un sujet de journal fait 32 octets ; l’adresse correspond aux 20 derniers.
- Les txid Bitcoin sont affichés dans l’ordre inverse de leur ordre interne en octets ; stockez la forme affichée.
- Adresses Bitcoin : les adresses bech32 et bech32m (
bc1…) ne distinguent pas les majuscules des minuscules et s’écrivent conventionnellement en minuscules, mais les adresses Base58 (1…,3…) sont sensibles à la casse. Convertir une adresse Base58 en minuscules la détruit. - Les quantités hexadécimales provenant d’Ethereum JSON-RPC (
"0x1406f52") doivent être analysées en base 16 ; les lire comme des chaînes décimales ou supprimer le préfixe produit des valeurs absurdes.
Identifiants de chaîne
Une même adresse peut exister sur des dizaines de chaînes EVM, contrôlées par la même clé mais détenant des soldes sans rapport entre eux ; les hachages de transaction ont partout le même format. Chaque ligne doit comporter un identifiant de chaîne. Les chaînes EVM utilisent le chain ID, introduit pour la protection contre les attaques par rejeu par EIP-155 et renvoyé par eth_chainId :
| Chaîne | Chain ID |
|---|---|
| Ethereum mainnet | 1 |
| OP Mainnet | 10 |
| BNB Smart Chain | 56 |
| Gnosis | 100 |
| Polygon PoS | 137 |
| Base | 8453 |
| Arbitrum One | 42161 |
| Avalanche C-Chain | 43114 |
| Sepolia (testnet) | 11155111 |
Un registre de chain IDs tenu à jour par la communauté est la référence habituelle pour les autres chaînes. Utilisez eth_chainId plutôt que net_version, dont l’identifiant réseau peut différer du chain ID. Excluez explicitement les réseaux de test et traitez les jetons pontés comme des actifs distincts, avec leurs propres adresses de contrat sur chaque chaîne ; la leçon Les L2, les chaînes latérales et les ponts comme sources de données explique comment cartographier les flux entre ces chaînes. Bitcoin n’a pas de chain ID, mais ses réseaux de test utilisent des préfixes d’adresse différents (tb1… au lieu de bc1…).
Déduplication et clés naturelles
Les doublons apparaissent à cause de récupérations rétroactives qui se chevauchent, de requêtes relancées, de réorganisations (la même transaction enregistrée dans deux blocs) et d’exports fusionnés. Définir une clé naturelle pour chaque type d’enregistrement permet de les détecter :
| Enregistrement | Clé naturelle |
|---|---|
| Bloc | block_hash |
| Transaction Bitcoin | block_hash, txid |
| Sortie Bitcoin | txid, vout |
| Transaction EVM | chain_id, tx_hash |
| Journal EVM | chain_id, block_hash, log_index |
| Trace EVM | chain_id, tx_hash, trace_address |
Pourquoi une transaction Bitcoin a-t-elle besoin du hachage du bloc ? Parce que l’unicité des txid n’est pas garantie : deux premières transactions coinbase ont été répétées à l’identique, dans les blocs 91 842 et 91 880. BIP 30 les a reconnues comme des exceptions historiques, puis BIP 34 a rendu cela impossible en exigeant que la hauteur du bloc figure dans chaque transaction coinbase. La requête ci-dessous conserve une copie de chaque journal et uniquement les journaux provenant de blocs canoniques :
-- Une ligne par journal, uniquement pour les blocs canoniques
WITH ranked AS (
SELECT l.*,
ROW_NUMBER() OVER (
PARTITION BY l.chain_id, l.block_hash, l.log_index
ORDER BY l.ingested_at DESC) AS rn
FROM raw_logs l
)
SELECT *
FROM ranked
WHERE rn = 1
AND block_hash IN (SELECT block_hash FROM canonical_blocks);
ROW_NUMBER numérote les copies de chaque clé et conserve celle qui a été ingérée le plus récemment ; le filtre final supprime les journaux provenant de blocs remplacés à la suite d’une réorganisation. Les doublons logiques comptent aussi : compter un journal Transfer et la trace du même appel de jeton, ou de l’ETH natif et du WETH, revient à compter la valeur deux fois.
Enregistrer la provenance
La provenance désigne l’enregistrement de l’origine d’un jeu de données et de la manière dont il a été produit. Associez-en un à chaque extraction qui étaye une conclusion :
{
"dataset": "token_transfers_to_target",
"chain_id": 1,
"block_range": [21000000, 21000999],
"anchor_block_hash": "0x5e1c…a7d0",
"finality": "finalized",
"source": "own archive node",
"client_version": "Geth/v1.16.x",
"method": "eth_getLogs",
"query_file": "queries/transfers_to_target.sql",
"retrieved_at_utc": "2026-10-28T14:05:00Z",
"row_count": 37,
"output_sha256": "b94d…e9f1"
}
La plage de blocs et le hachage d’ancrage fixent les données ; l’état de finalité indique qu’elles ne peuvent plus changer ; la source, la version du client et la méthode précisent comment elles ont été obtenues ; le fichier de requête consigne la transformation ; l’heure de récupération en UTC les date ; et le nombre de lignes ainsi que le hachage SHA-256 du fichier de sortie permettent à quiconque de confirmer qu’il détient les mêmes données. Un réviseur disposant d’un nœud peut relancer la requête et comparer les résultats. La leçon Performance, coût et reproductibilité étend cette approche aux requêtes versionnées dans un entrepôt de données.
Erreurs courantes dans les données
| Erreur | Symptôme | Prévention |
|---|---|---|
| Montants à virgule flottante | Totaux inexacts dans les derniers chiffres | Unités de base entières, analyse décimale |
| Mauvais nombre de décimales | Montants décalés d’un facteur de 10^12 (6 interprété comme 18) | Lire decimals pour chaque contrat et chaque chaîne |
| Fuseau horaire local | Totaux quotidiens décalés d’un jour à l’autre | UTC de bout en bout |
| Chain ID manquant | Activité de chaîne latérale incluse dans les totaux du mainnet | chain_id sur chaque ligne et dans chaque jointure |
| Transactions échouées comptabilisées | Valeur qui n’a jamais été transférée | Filtrer avec status = 1 et sur les erreurs de trace |
| Lignes de blocs réorganisés conservées | Transactions absentes de la chaîne finale | Joindre aux blocs canoniques par hachage |
| Identifiants dont la casse a été modifiée | Les jointures ne trouvent pas certaines lignes ; les adresses Base58 sont endommagées | Convertir uniquement les valeurs hexadécimales en minuscules |
Une vérification finale simple permet d’en détecter plusieurs à la fois : choisissez trois à cinq lignes qui comptent le plus pour une conclusion et vérifiez chacune d’elles auprès d’un nœud ou d’une deuxième source, en comparant le hachage du bloc, le montant en unités de base et l’horodatage. Si une seule ligne ne concorde pas, arrêtez-vous et cherchez pourquoi avant de faire confiance à l’agrégat.
Points clés pour l'examen : satoshi, wei, gwei et décimales des jetons, et pourquoi les montants restent des entiers ; risques liés à la virgule flottante et au dépassement de capacité sur 64 bits (un BIGINT ne contient qu’environ 9,22 ETH en wei) ; horodatages UTC et classement par hauteur ; sommes de contrôle EIP-55 par opposition au stockage en minuscules et sensibilité à la casse de Base58 ; chain IDs (EIP-155, eth_chainId) et raison pour laquelle chaque ligne doit en comporter un ; clés naturelles pour la déduplication et txid dupliqués reconnus par BIP 30 ; champs d’un enregistrement de provenance ; erreurs courantes dans les données et moyens de les prévenir.Questions d’entraînement
Vérifiez votre compréhension. Ces questions ne comptent pas pour l’examen.
Connectez-vous pour enregistrer votre progression et reprendre là où vous vous êtes arrêté. C’est gratuit. Se connecter