Unformatierte Blockchain-Daten lesen · Lektion 6 von 6
Datenqualität, Normalisierung und Herkunftsnachweis
9 Min. Lesezeit
Die meisten falschen Zahlen in Blockchain-Analysen entstehen nicht durch schlechte Heuristiken, sondern durch ganz gewöhnliche Datenfehler: ein Betrag, der um den Faktor 10^12 abweicht, eine Tagesgrenze in der falschen Zeitzone, eine doppelt gezählte Übertragung oder eine Polygon-Transaktion, die in Ethereum-Summen einfließt. Solche Fehler passieren leicht und sind in einem fertigen Diagramm schwer zu erkennen. In dieser Lektion werden die Normalisierungsregeln erläutert, die sie verhindern, sowie der Herkunftsnachweis, mit dem jede Person Ihre Arbeit überprüfen kann.
Einheiten: Bis zum Schluss Ganzzahlen verwenden
Blockchains speichern Beträge als Ganzzahlen der kleinsten Einheit. Dezimalstellen sind eine Darstellungsform.
| Asset | Basiseinheit | Umrechnung |
|---|---|---|
| Bitcoin | Satoshi | 1 BTC = 100.000.000 sat (10^8) |
| Ether | Wei | 1 ETH = 10^18 Wei; 1 Gwei = 10^9 Wei |
| ERC-20-Token | Roheinheiten | Durch 10^decimals teilen; der Wert wird aus dem Token-Contract ausgelesen |
Token-Dezimalstellen variieren: 18 ist üblich, aber USD Coin verwendet 6 und Wrapped Bitcoin verwendet 8. Der Wert decimals ist im ERC-20-Standard optional und dient nur der Information: Die Guthaben des Contracts sind immer rohe Ganzzahlen. Lesen Sie den Wert aus jedem Contract auf jeder Chain aus, statt ihn anzunehmen, denn eine über eine Bridge übertragene Version eines Tokens kann sich vom Original unterscheiden. Die Folgen einer falschen Annahme sind gravierend: Ein Rohbetrag von 2.500.000.000 entspricht bei 6 Dezimalstellen 2.500 Token, bei 18 Dezimalstellen jedoch 0,0000000025 – ein Faktor von 10^12.
Daraus ergeben sich zwei Fallstricke. Erstens Gleitkommazahlen. Bitcoin Core gibt Beträge in BTC als JSON-Zahlen wie 0.00485900 zurück. Ein unachtsamer Parser wandelt sie in binäre Gleitkommazahlen um, die die meisten Dezimalbrüche nicht exakt darstellen können. Dadurch weichen Summen geringfügig ab und Abstimmungen schlagen fehl. Parsen Sie Beträge als Dezimalstrings und wandeln Sie sie sofort in ganzzahlige Satoshis um. Zweitens Überlauf. Ethereum-Beträge sind vorzeichenlose 256-Bit-Ganzzahlen und können bis zu etwa 1,16 × 10^77 betragen. Ein vorzeichenbehafteter 64-Bit-BIGINT reicht nur bis 9.223.372.036.854.775.807, was in Wei gerade einmal etwa 9,22 ETH entspricht. Selbst ein 38-stelliger DECIMAL kann nicht jeden möglichen Wert aufnehmen. Speichern Sie Rohbeträge als Text oder in einem breit angelegten numerischen Datentyp und wandeln Sie sie erst im letzten Schritt um:
-- Rohbeträge bleiben exakt; die Umrechnung erfolgt nur für die Anzeige
SELECT tt.tx_hash,
tt.log_index,
tt.raw_amount, -- exakte Ganzzahl wird als Text gespeichert
CAST(tt.raw_amount AS DECIMAL(38, 0))
/ POWER(10, tk.decimals) AS amount_display -- Näherungswert
FROM token_transfers tt
JOIN tokens tk
ON tk.chain_id = tt.chain_id
AND tk.contract_address = tt.contract_address;
Die Verknüpfung anhand von chain_id und Contract-Adresse wählt für jeden Token auf jeder Chain den richtigen Wert für decimals aus. Die Umwandlung schlägt bei Werten mit mehr als 38 Ziffern fehl. So werden Spam-Token mit absurden Umlaufmengen erkannt, statt Summen unbemerkt zu verfälschen. Einige Engines bieten stattdessen 76-stellige Dezimalzahlen oder native 256-Bit-Datentypen. POWER gibt in den meisten Engines eine Gleitkommazahl zurück, daher ist amount_display ein Näherungswert, der sich nur für die Anzeige eignet; für Summen und Abstimmungen wird raw_amount verwendet. Teilen Sie nicht stattdessen durch einen DECIMAL(38, 0): Einige Engines übernehmen für das Ergebnis die Skala der Operanden – hier also null – und kürzen es unbemerkt auf eine Ganzzahl.
Zeit: UTC und Blockzeit
Blockzeitstempel sind Unix-Zeit: Sekunden seit dem 1. Januar 1970, UTC. Speichern Sie alle Zeitstempel in UTC, gruppieren Sie Tage und Monate in UTC und geben Sie dies im Bericht an. Eine Umrechnung in eine lokale Zeitzone verschiebt Transaktionen über Tagesgrenzen hinweg. Durch die Umstellung auf Sommerzeit entstehen außerdem Stunden, die zweimal oder gar nicht vorkommen. Eine am 31. März um 23:30 UTC geschürfte Transaktion gehört in einem UTC-Bericht zum März, für eine Analystin oder einen Analysten, deren Werkzeuge UTC+2 stillschweigend anzeigen, jedoch zum 1. April. Vergleichen zwei Teams auf diese Weise erstellte Monatssummen, stimmen sie nicht überein, ohne dass einer der Teams den Grund dafür bemerkt. Speichern Sie Zeitstempel als UTC-Werte (oder als rohe Unix-Sekunden) und rechnen Sie sie nur in der Darstellungsebene um. Kennzeichnen Sie dabei die verwendete Zeitzone.
Beachten Sie, was der Zeitstempel bedeutet. Er bezeichnet den Zeitpunkt des Blocks, nicht den Zeitpunkt der Signierung oder Übertragung, und bei Bitcoin kann er vor dem Zeitstempel des vorherigen Blocks liegen. Ordnen Sie Ereignisse nach Höhe (und ihrer Position innerhalb des Blocks), nicht nach Zeitstempel. Berücksichtigen Sie beim Abgleich von Aktivitäten über mehrere Chains hinweg die unterschiedlichen Blockintervalle und Zeitstempelregeln.
Kennungen und ihre Formate
Dieselbe Kennung kann auf verschiedene Arten geschrieben werden, und Verknüpfungen schlagen unbemerkt fehl, wenn die Formate voneinander abweichen.
- EVM-Adressen erscheinen in EIP-55-Prüfsummenform mit Groß- und Kleinbuchstaben (
0xA1b2…9F) in Wallets und Explorern, in Logs und den meisten Datensätzen jedoch in Kleinbuchstaben. Wandeln Sie alle Hex-Adressen und -Hashes für die Speicherung und Verknüpfung in Kleinbuchstaben um. Bewahren Sie die Prüfsummenform nur für die Anzeige auf. - Aufgefüllte Werte: Eine Adresse in einem Log-Thema ist 32 Byte lang; die Adresse sind die letzten 20 Byte.
- Bitcoin-Txids werden in umgekehrter Reihenfolge ihrer internen Byte-Reihenfolge angezeigt; speichern Sie die Anzeigeform.
- Bitcoin-Adressen: Bech32- und Bech32m-Adressen (
bc1…) unterscheiden nicht zwischen Groß- und Kleinschreibung und werden üblicherweise in Kleinbuchstaben geschrieben. Base58-Adressen (1…,3…) unterscheiden dagegen zwischen Groß- und Kleinschreibung. Wenn Sie eine Base58-Adresse in Kleinbuchstaben umwandeln, wird sie unbrauchbar. - Hexadezimale Mengen aus Ethereum JSON-RPC (
"0x1406f52") müssen als Basis 16 geparst werden. Werden sie als Dezimalstrings gelesen oder wird das Präfix entfernt, ergibt sich Unsinn.
Chain-Kennungen
Dieselbe Adresse kann auf Dutzenden von EVM-Chains existieren und durch denselben Schlüssel kontrolliert werden, während sie dort nicht zusammenhängende Guthaben hält. Transaktions-Hashes haben überall dasselbe Format. Jede Zeile benötigt eine Chain-Kennung. EVM-Chains verwenden die durch EIP-155 zum Schutz vor Replay-Angriffen eingeführte Chain-ID, die von eth_chainId zurückgegeben wird:
| Chain | 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 |
Für andere Chains ist ein von der Community gepflegtes Chain-ID-Register die übliche Referenz. Verwenden Sie eth_chainId statt net_version, dessen Network-ID von der Chain-ID abweichen kann. Schließen Sie Testnets explizit aus und behandeln Sie über Bridges übertragene Token als eigenständige Assets mit eigenen Contract-Adressen auf jeder Chain. Die Lektion Layer 2s, Sidechains und Bridges als Datenquellen behandelt die Zuordnung von Transaktionsflüssen zwischen ihnen. Bitcoin hat keine Chain-ID, aber seine Testnetze verwenden andere Adresspräfixe (tb1… statt bc1…).
Deduplizierung und natürliche Schlüssel
Duplikate entstehen durch überlappende Backfills, wiederholte Anfragen, Reorgs (dieselbe Transaktion wird in zwei Blöcken aufgezeichnet) und zusammengeführte Exporte. Wenn Sie für jeden Datensatztyp einen natürlichen Schlüssel definieren, lassen sich Duplikate erkennen:
| Datensatz | Natürlicher Schlüssel |
|---|---|
| Block | block_hash |
| Bitcoin-Transaktion | block_hash, txid |
| Bitcoin-Output | txid, vout |
| EVM-Transaktion | chain_id, tx_hash |
| EVM-Log | chain_id, block_hash, log_index |
| EVM-Trace | chain_id, tx_hash, trace_address |
Warum benötigt eine Bitcoin-Transaktion den Block-Hash? Weil die Eindeutigkeit von Txids nicht garantiert ist: Zwei frühe Coinbase-Transaktionen wurden in den Blöcken 91.842 und 91.880 exakt wiederholt. BIP 30 bestätigte diese Ausnahmen nachträglich; durch BIP 34 wurde dies später unmöglich, da es die Aufnahme der Blockhöhe in jede Coinbase-Transaktion vorschreibt. Die folgende Abfrage behält eine Kopie jedes Logs und berücksichtigt nur Logs aus kanonischen Blöcken:
-- Eine Zeile pro Log, nur aus kanonischen Blöcken
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 nummeriert die Kopien jedes Schlüssels und behält die zuletzt eingelesene. Der abschließende Filter verwirft Logs aus Blöcken, die durch einen Reorg ersetzt wurden. Auch logische Duplikate sind relevant: Wenn sowohl ein Transfer-Log als auch der Trace desselben Token-Aufrufs oder sowohl native ETH als auch WETH gezählt werden, wird der Wert doppelt gezählt.
Herkunftsnachweis dokumentieren
Herkunftsnachweis ist die Aufzeichnung, woher ein Datensatz stammt und wie er erstellt wurde. Fügen Sie jedem Extrakt, der eine Feststellung stützt, einen solchen Nachweis bei:
{
"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"
}
Der Blockbereich und der Anker-Block-Hash fixieren die Daten; der Finalitätsstatus zeigt, dass sie sich nicht ändern können. Quelle, Client-Version und Methode geben an, wie die Daten abgerufen wurden; die Abfragedatei dokumentiert die Transformation; der Abrufzeitpunkt in UTC datiert sie. Zeilenanzahl und SHA-256-Hash der Ausgabedatei ermöglichen es allen, zu bestätigen, dass sie dieselben Daten haben. Eine prüfende Person mit Zugriff auf einen Node kann die Abfrage erneut ausführen und die Ergebnisse vergleichen. Die Lektion Leistung, Kosten und Reproduzierbarkeit erweitert dies um versionierte Abfragen in einem Data Warehouse.
Häufige Datenfehler
| Fehler | Symptom | Vorbeugung |
|---|---|---|
| Gleitkomma-Beträge | Summen weichen in den letzten Ziffern ab | Ganzzahlige Basiseinheiten, Dezimal-Parsing |
| Falsche Dezimalstellen | Beträge weichen um 10^12 ab (6 als 18 behandelt) | decimals pro Contract und Chain auslesen |
| Lokale Zeitzone | Tagessummen verschieben sich zwischen Tagen | Durchgehend UTC verwenden |
| Fehlende Chain-ID | Sidechain-Aktivität fließt in Mainnet-Summen ein | chain_id in jeder Zeile und Verknüpfung |
| Fehlgeschlagene Transaktionen mitgezählt | Wert, der nie übertragen wurde | Nach status = 1 und Trace-Fehlern filtern |
| Reorganisierte Zeilen beibehalten | Transaktionen, die in der endgültigen Chain fehlen | Über den Hash mit kanonischen Blöcken verknüpfen |
| Kennungen mit veränderter Groß-/Kleinschreibung | Verknüpfungen übersehen Zeilen; Base58-Adressen sind beschädigt | Nur Hexadezimalwerte in Kleinbuchstaben umwandeln |
Ein einfacher Abschlusstest deckt viele dieser Fehler auf einmal auf: Wählen Sie drei bis fünf Zeilen aus, die für eine Feststellung besonders wichtig sind, und prüfen Sie jede anhand eines Nodes oder einer zweiten Quelle. Vergleichen Sie dabei Block-Hash, Betrag in Basiseinheiten und Zeitstempel. Wenn auch nur eine Zeile abweicht, halten Sie inne und finden Sie die Ursache, bevor Sie den aggregierten Wert für verlässlich halten.
Prüfungsschwerpunkt: Satoshi, Wei, Gwei und Token-Dezimalstellen sowie die Gründe dafür, Beträge als Ganzzahlen beizubehalten; Risiken durch Gleitkommazahlen und 64-Bit-Überläufe (BIGINT kann in Wei nur etwa 9,22 ETH aufnehmen); UTC-Zeitstempel und die Sortierung nach Höhe; EIP-55-Prüfsummen gegenüber der Speicherung in Kleinbuchstaben sowie die Groß-/Kleinschreibungssensitivität von Base58; Chain-IDs (EIP-155, eth_chainId) und warum jede Zeile eine benötigt; natürliche Schlüssel für die Deduplizierung und die doppelten Txids gemäß BIP 30; die Felder eines Herkunftsnachweises; häufige Datenfehler und wie sie sich vermeiden lassen.