Transaktionsgraphen erstellen · Lektion 1 von 6
Transaktionsgraphen: Knoten, Kanten und Richtung
8 Min. Lesezeit
Fast jede Frage zur Nachverfolgung – von „Wohin sind die gestohlenen Gelder geflossen?“ bis „Ist diese Einzahlung einem sanktionierten Dienst ausgesetzt?“ – ist eine Frage zu einem Graphen. Bevor Sie etwas nachverfolgen, bewerten oder visualisieren können, müssen Sie entscheiden, was die Knoten und Kanten dieses Graphen sind. Diese Entscheidungen legen stillschweigend fest, welche Antworten möglich sind. In dieser Lektion erfahren Sie, wie Sie Rohtransaktionen in einen Graphen umwandeln, den Sie nachvollziehbar begründen können.
Drei Graphen aus denselben Daten
Dieselben Blockdaten lassen sich auf drei Arten modellieren, und Analysten wechseln ständig zwischen ihnen.
| Graph | Knoten | Kanten | Am besten geeignet für |
|---|---|---|---|
| Transaktionsgraph | Transaktionen (und bei UTXO-Chains die von ihnen erzeugten Outputs) | Ein von einer Transaktion erzeugter und von einer anderen ausgegebener Output | Exakte, verlustfreie Nachverfolgung von UTXOs |
| Adressgraph | Adressen | Von einer Adresse an eine andere gesendeter Wert | Chains mit Kontomodell, schnelle Übersichten |
| Entitätsgraph | Cluster oder gekennzeichnete Dienste | Aggregierte Flüsse zwischen Entitäten | Berichte, Exposition, Gegenparteianalyse |
Der Transaktionsgraph liegt den Rohdaten am nächsten. Bei Bitcoin bleibt jeder Output entweder unausgegeben oder wird genau von einem späteren Input verbraucht. Dadurch bilden die Ausgabebeziehungen einen präzisen gerichteten azyklischen Graphen: Geld kann nicht rückwärts durch die Zeit fließen. In diesem Graphen wird nichts abgeleitet.
Der Adressgraph fasst Transaktionen zusammen, sodass jede Adresse zu einem Knoten wird. Für Ethereum und andere Chains mit Kontomodell ist das naheliegend, denn eine Überweisung erfolgt tatsächlich von einem Konto an ein anderes. Bei UTXO-Chains führt dies zu Mehrdeutigkeit, wie das folgende Rechenbeispiel zeigt.
Der Entitätsgraph ersetzt Adressen durch Cluster („Hot Wallets von Börse X“, „Diebstahl-Cluster 1“). Die meisten Berichte zeigen diesen Graphen, doch jeder Knoten hängt von Clustering und Zuordnung ab, die eigene Fehlerraten mit sich bringen (siehe die Lektion Was Clustering ist und warum es wichtig ist). Halten Sie die unteren Ebenen verfügbar, damit sich jede Kante in einem Entitätsgraphen zu den zugrunde liegenden Transaktionen zurückverfolgen lässt.
Viele Tools verwenden außerdem ein Hybridmodell: einen bipartiten Graphen mit zwei Arten von Knoten, Adressen und Transaktionen. Die Kanten führen von einer Adresse zu einer Transaktion (einem Input) und von einer Transaktion zu einer Adresse (einem Output). So bleibt die Genauigkeit des Transaktionsgraphen erhalten, während zugleich sichtbar wird, wer welche Werte hält. Auf diese Weise stellen viele Visualisierungstools für die Nachverfolgung Flüsse dar.
Kanten: Richtung, Gewicht und Zeit
Eine nützliche Kante enthält mehr als „A hat an B gezahlt“. Speichern Sie mindestens Folgendes:
- Richtung: Quelle und Ziel. Bei der Vorwärtsverfolgung folgen Sie ausgehenden Kanten, bei der Rückwärtsverfolgung eingehenden Kanten.
- Gewicht: den Betrag in der nativen Einheit des Assets nach der Umrechnung der Dezimalstellen (BTC, ETH oder Token geteilt durch 10^Dezimalstellen), außerdem optional den Wert zum Zeitpunkt der Überweisung in einer Referenzwährung, wobei die Preisquelle festgehalten wird.
- Zeit: Blockhöhe und Zeitstempel des Blocks in UTC. Die Höhe ordnet Ereignisse zuverlässig; Zeitstempel sind Näherungswerte (siehe die Lektion Blöcke und Header als Daten).
- Identität: Transaktions-Hash sowie Output-Index (UTXO), Log-Index oder Trace-Position (Kontomodell), damit die Kante eindeutig ist und mit einem Knoten abgeglichen werden kann.
- Asset: native Coin oder Token-Contract-Adresse. Addieren Sie niemals Kanten verschiedener Assets.
Zwei Adressen können durch viele Kanten zu unterschiedlichen Zeitpunkten verbunden sein; die Struktur ist also ein Multigraph. Parallele Kanten zu einer einzigen Kante „insgesamt gesendet“ zusammenzufassen, ist für ein Diagramm in Ordnung, zerstört aber die Reihenfolge, auf die die Nachverfolgung angewiesen ist: Gelder, die erst nach einer Zahlung eingegangen sind, können diese nicht finanziert haben.
Von einer UTXO-Transaktion zu Kanten
Hier sehen Sie eine dekodierte Bitcoin-Transaktion, ergänzt um die Adresse und den Wert jedes ausgegebenen Outputs (Roh-Inputs verweisen nur auf einen Outpoint; ein Indexer oder eine zweite Abfrage liefert diese Felder):
{
"txid": "4f3a…c21e",
"block_height": 871204,
"vin": [
{"prev_txid": "9b1e…04aa", "vout": 1, "address": "bc1q…7k3f", "value": 0.60000000},
{"prev_txid": "c07d…e912", "vout": 0, "address": "bc1q…m2pz", "value": 0.50000000}
],
"vout": [
{"n": 0, "address": "bc1q…x9td", "value": 0.80000000},
{"n": 1, "address": "bc1q…h4wa", "value": 0.29000000}
]
}
Die Inputs summieren sich auf 1,10 BTC und die Outputs auf 1,09 BTC, die Gebühr beträgt also 0,01 BTC.
Im Transaktionsgraphen ist dies ein Knoten mit zwei eingehenden Kanten (den Outpoints 9b1e…04aa:1 und c07d…e912:0) und zwei ausgehenden Kanten (Outputs 0 und 1). Es sind keine Annahmen erforderlich.
Im Adressgraphen müssen Sie Kanten von Adresse zu Adresse anlegen. Bitcoin gibt jedoch nicht an, welcher Input welchen Output bezahlt hat. Die beiden Inputs werden zusammengeführt. Wenn Sie jede mögliche Zuordnung einzeichnen, erhalten Sie vier Kanten (2 Inputs × 2 Outputs), und Sie müssen entscheiden, wie Sie sie gewichten. Üblicherweise wird eine proportionale Zuordnung verwendet: Der erste Input steuerte 0,60 ÷ 1,10 = 54,55 % des Werts bei und erhält daher 54,55 % jedes Outputs gutgeschrieben.
| Kante | Berechnung | Gewicht (BTC) |
|---|---|---|
| bc1q…7k3f → bc1q…x9td | 0,80 × 0,5455 | 0,4364 |
| bc1q…7k3f → bc1q…h4wa | 0,29 × 0,5455 | 0,1582 |
| bc1q…m2pz → bc1q…x9td | 0,80 × 0,4545 | 0,3636 |
| bc1q…m2pz → bc1q…h4wa | 0,29 × 0,4545 | 0,1318 |
Die Gewichte summieren sich auf 1,09 BTC, und die Gebühr von 0,01 BTC verlässt den Graphen (manche Analysten fügen einen Senkenknoten „Gebühr“ hinzu, damit der Wert erhalten bleibt). Eine Transaktion mit 50 Inputs und 50 Outputs würde 2.500 solcher Kanten erzeugen. Deshalb können große Konsolidierungen und Sammelauszahlungen einen Adressgraphen überfrachten. Ob bc1q…h4wa eine Zahlung oder Wechselgeld ist, das an den Sender zurückgeht, ist eine separate Schlussfolgerung (siehe die Lektion Heuristiken zur Wechselgelderkennung).
Von Kontomodell-Daten zu Kanten
Bei Chains nach dem Vorbild von Ethereum stammen Kanten aus drei Quellen. Ein Graph, der nur eine davon berücksichtigt, lässt Geldflüsse aus:
- Transaktionen auf oberster Ebene mit einem von null verschiedenen
value: vom Signierer zurto-Adresse. - Interne Überweisungen nativer Coins, die Contracts während der Ausführung tätigen und nur in Traces erscheinen (
call-Frames mit einem Wert). - Token-Überweisungen, dekodiert aus Logs: Ein ERC-20-Ereignis
Transfer(address,address,uint256)hat das Thema topic00xddf252ad…b3ef, den Sender und Empfänger in den Themen 1 und 2 sowie den Rohbetrag im Datenfeld.
Dabei sind drei Punkte zu beachten. Schließen Sie rückgängig gemachte Transaktionen (Receipt-status = 0) und rückgängig gemachte Trace-Frames aus: Sie haben nichts bewegt, auch wenn der Sender weiterhin Gas gezahlt hat. Behandeln Sie Contracts als Knoten, aber kennzeichnen Sie sie, denn ein DEX-Router oder Token-Contract ist ein Mechanismus und keine Gegenpartei. Denken Sie außerdem daran, dass ein einzelner Swap üblicherweise zwei Kanten in verschiedenen Assets erzeugt: Nutzer → Pool in einem Token und Pool → Nutzer in einem anderen. Die Nachverfolgung von Werten über dieses Kantenpaar wird in der Lektion Chainübergreifende Nachverfolgung behandelt.
Eine Kantenliste in SQL erstellen
Die meiste Grapharbeit beginnt in einem Data Warehouse mit einer flachen Kantenliste. Die folgende Abfrage erstellt eine solche Liste für einen Ethereum-Blockbereich. Tabellen- und Spaltennamen entsprechen einem typischen öffentlichen ETL-Schema; prüfen Sie Ihr eigenes Schema.
-- Kantenliste: natives ETH (aus Traces) plus ERC-20-Überweisungen
SELECT block_number, block_timestamp, transaction_hash AS txid,
from_address AS src, to_address AS dst,
'ETH' AS asset, value / 1e18 AS amount -- Wei zu ETH
FROM traces
WHERE value > 0 AND status = 1 -- nur erfolgreiche Frames
AND block_number BETWEEN 21000000 AND 21050000
UNION ALL
SELECT tt.block_number, tt.block_timestamp, tt.transaction_hash,
tt.from_address, tt.to_address,
tt.token_address, tt.value / POWER(10, tk.decimals)
FROM token_transfers tt
JOIN tokens tk ON tk.address = tt.token_address -- Dezimalstellen je Token
WHERE tt.block_number BETWEEN 21000000 AND 21050000;
Die erste Hälfte bezieht Bewegungen nativer Coins aus der Traces-Tabelle. In diesem Schema enthält sie sowohl Aufrufe auf oberster Ebene als auch interne Aufrufe und behält nur erfolgreiche Frames mit Wert bei. Die zweite Hälfte fügt Token-Überweisungen hinzu und rechnet Rohzahlen anhand der Dezimalstellen des jeweiligen Tokens um. UNION ALL behält jede Zeile bei, denn Duplikate stehen hier für tatsächliche, separate Überweisungen. Beide Hälften enthalten denselben Blockfilter. So bleibt der Scan begrenzt und das Ergebnis reproduzierbar. Große Ganzzahlwerte können in manchen Engines den zulässigen Wertebereich numerischer Datentypen überschreiten. Deshalb speichern viele Data Warehouses value als String oder als 256-Bit-Dezimalzahl.
Praktische Entscheidungen vor der Nachverfolgung
- Begrenzen Sie den Graphen. Legen Sie vor der Abfrage einen Startpunkt, einen Zeitraum und eine Hop-Grenze fest. Eine unbegrenzte Erweiterung erreicht innerhalb weniger Hops jede Börse und ist dann kein Beleg mehr.
- Kennzeichnen Sie Superknoten. Hot Wallets von Börsen, Bridges und beliebte Contracts haben Millionen von Kanten. Markieren Sie sie als Endpunkte, statt sie weiter auszubauen.
- Wählen Sie den Speicher passend zur Fragestellung. Kantenlisten in SQL eignen sich gut für Aggregationen und Filter; Graphdatenbanken und -bibliotheken (wie NetworkX oder igraph) eignen sich besser für Pfadsuche und Zentralität. Viele Teams exportieren einen begrenzten Teilgraphen aus SQL in ein Graphtool.
- Halten Sie für jede Kante die Herkunft fest: txid, Index, Blockhöhe und die Abfrage, mit der sie erzeugt wurde. So kann ein anderer Analyst denselben Graphen neu erstellen.
Prüfungsschwerpunkt: der Unterschied zwischen Transaktions-, Adress- und Entitätsgraphen und wofür sich jeder eignet; warum der UTXO-Ausgabengraph exakt ist, während der Adressgraph eine Zuordnungsannahme erfordert; Berechnung proportionaler Kantengewichte für eine Transaktion mit mehreren Inputs und Outputs; erforderliche Attribute jeder Kante (Richtung, Betrag, Asset, Blockhöhe, UTC-Zeit, txid und Index); die drei Quellen für Kanten im Kontomodell (Transaktionen, Traces, Token-Logs) und warum rückgängig gemachte Transaktionen ausgeschlossen werden; warum parallele Kanten ihre Zeitstempel behalten müssen; einen Graphen begrenzen und Superknoten als Endpunkte behandeln.