Tracciamento e grafi delle transazioni · Lezione 1 di 6
Transaction graphs and graph metrics
13 min di lettura
Questa lezione non è ancora disponibile in Italiano, quindi è mostrata in inglese.
Almost every tracing question, from "where did the stolen funds go?" to "is this deposit exposed to a sanctioned service?", is a question about a graph. What you choose as nodes and edges quietly decides which answers are possible, and once the graph exists you need ways to find what matters without reading thousands of edges by hand. This lesson shows how to build a graph you can defend, then the metrics and recurring shapes analysts use to read it, and why none of them is proof on its own.
Three graphs from the same data
The same block data can be modeled in three ways, and analysts move between them constantly.
| Graph | Nodes | Edges | Best for |
|---|---|---|---|
| Transaction graph | Transactions (and, in UTXO chains, the outputs they create) | An output created by one transaction and spent by another | Exact, lossless UTXO tracing |
| Address graph | Addresses | Value sent from one address to another | Account-model chains, quick overviews |
| Entity graph | Clusters or labeled services | Aggregated flows between entities | Reporting, exposure, counterparty analysis |
The transaction graph is closest to the raw data. In Bitcoin every output either sits unspent or is consumed by exactly one later input, so spending relationships form a precise directed acyclic graph: money cannot flow backward in time, and nothing is inferred. The address graph makes each address one node. It is natural for account-model chains, where a transfer really goes from one account to another, but in UTXO chains it introduces ambiguity, as the example below shows. The entity graph replaces addresses with clusters ("Exchange X hot wallets"). Most reports show it, but every node depends on clustering and attribution, which have their own error rates (Lesson 4.1), so keep the lower layers expandable behind every entity edge.
Many visual tracing tools draw a hybrid bipartite graph with two kinds of node, addresses and transactions: edges run from an address into a transaction (an input) and from a transaction to an address (an output). It keeps the exactness of the transaction graph while showing who holds what.
Edges: direction, weight and time
A useful edge carries more than "A paid B". At minimum, store:
- Direction: source and destination. Tracing forward follows outgoing edges; tracing backward follows incoming ones.
- Weight: the amount in native units after decimal conversion, optionally with its value in a reference currency at the time, citing the price source.
- Time: block height and block timestamp in UTC. Height orders events reliably; timestamps are approximate (Lesson 2.1).
- Identity: transaction hash plus the output index (UTXO) or the log index or trace position (account model), so the edge is unique and can be checked against a node.
- Asset: native coin or token contract address. Never add up edges in different assets.
Two addresses can be linked by many edges at different times (a multigraph). Aggregating them into one "total sent" edge suits a chart but destroys the ordering tracing depends on: funds that arrived after a payment cannot have financed it.
From a UTXO transaction to edges
Take a decoded Bitcoin transaction 4f3a…c21e at block 871,204, enriched with the address and value of each spent output (raw inputs only reference an outpoint, so an indexer or a second lookup supplies these fields):
| Side | Reference | Address | Value (BTC) |
|---|---|---|---|
| Input | 9b1e…04aa:1 | bc1q…7k3f | 0.60000000 |
| Input | c07d…e912:0 | bc1q…m2pz | 0.50000000 |
| Output 0 | 4f3a…c21e:0 | bc1q…x9td | 0.80000000 |
| Output 1 | 4f3a…c21e:1 | bc1q…h4wa | 0.29000000 |
Inputs total 1.10 BTC and outputs 1.09 BTC, so the fee is 0.01 BTC.
In the transaction graph this is one node with two incoming edges (the outpoints) and two outgoing edges (the outputs); no assumption is needed. In the address graph, Bitcoin does not say which input paid which output, because inputs are pooled. Every pairing gives four edges (2 inputs × 2 outputs), and you must choose their weights. The common choice is proportional allocation: the first input supplied 0.60 ÷ 1.10 = 54.55% of the value, so it is credited with 54.55% of each output, and the second with 45.45%.
| Edge | Calculation | Weight (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 |
The weights sum to 1.09 BTC; the fee leaves the graph (some analysts add a "fee" sink node so value is conserved). A 50-input, 50-output transaction would create 2,500 such edges, which is why consolidations and batch payouts swamp address graphs. Whether bc1q…h4wa is a payment or change is a separate inference (Lesson 4.2), and proportional weighting is only one allocation rule (Lesson 3.3).
From account-model data to edges
On Ethereum-style chains, edges come from three sources, and a graph built from only one of them will miss money:
- Top-level transactions with a non-zero
value: from the signer to thetoaddress. - Internal transfers of native coin made by contracts during execution, which appear only in traces (
callframes with value). - Token transfers, decoded from logs: an ERC-20
Transfer(address,address,uint256)event has topic00xddf252ad…b3ef, the sender and recipient in topics 1 and 2, and the raw amount in the data field.
Exclude reverted transactions (receipt status = 0) and reverted trace frames: they moved nothing, although the sender still paid gas. Treat contracts as labeled nodes, because a DEX router or token contract is a mechanism, not a counterparty. A swap usually produces two edges in different assets, user → pool and pool → user (Lesson 5.2).
Most graph work starts as a flat edge list in a warehouse, built from the generic schema of Lesson 2.7 and saved as a table edges that the queries in this module and the next reuse:
-- Edge list (saved as edges): native ETH from traces plus ERC-20 transfers
SELECT block_number, block_timestamp, transaction_hash,
from_address AS src, to_address AS dst,
'ETH' AS asset, value / 1e18 AS amount -- wei to ETH
FROM traces
WHERE value > 0
AND (call_type IS NULL OR call_type = 'call') -- no delegatecall or staticcall
AND status = 1 AND tx_status = 1 -- successful frames only
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.amount_raw / POWER(10, tk.decimals)
FROM token_transfers AS tt
JOIN tokens AS tk ON tk.token_address = tt.token_address -- decimals per token
WHERE tt.block_number BETWEEN 21000000 AND 21050000;
The traces half includes top-level calls as well as internal ones, so it does not need the transactions table. UNION ALL keeps identical-looking rows, which are real, separate transfers, and the shared block range keeps the result bounded and reproducible. The inner join silently drops tokens missing from tokens; compare row counts (Lesson 2.9 covers the trace filters, Lesson 2.8 the joins).
Practical choices before you trace
- Bound the graph. Fix a start point, time window and hop limit before you query; unbounded expansion reaches every exchange within a few hops and stops being evidence.
- Mark supernodes. Exchange hot wallets, bridges and popular contracts have millions of edges; flag them as stopping points.
- Match storage to the question. SQL edge lists suit aggregation and filtering; graph databases and libraries such as NetworkX or igraph suit path finding and centrality, so many teams export a bounded subgraph into a graph tool.
- Keep provenance on every edge: txid, index, block height and the query that produced it, so another analyst can rebuild the same graph.
Degree and value flow
The simplest metrics are counts and sums per node, computed over a fixed time window and a single asset:
- In-degree: number of distinct counterparties that sent to the node. Out-degree: number it sent to.
- Weighted degree: total value received and sent.
- Pass-through ratio: value out ÷ value in over the window. A ratio near 1 with a short holding time marks an intermediary that forwards what it receives rather than keeping it.
- Activity span and holding time: first and last activity, and the time between receiving and sending.
These alone separate many roles. A personal wallet has low degree and holds funds for weeks. An exchange deposit address has a few inflows from different senders and outflows to one destination, the sweep wallet. A hot wallet has degrees in the tens of thousands. A laundering intermediary often has in-degree 1, out-degree 2 to 20, a pass-through ratio close to 1 and a holding time of minutes. The query below profiles one month of native-coin edges.
WITH io AS ( -- one row per side of each edge
SELECT dst AS address, src AS peer, amount AS amt_in, 0 AS amt_out, block_timestamp AS ts
FROM edges WHERE asset = 'ETH'
UNION ALL
SELECT src, dst, 0, amount, block_timestamp
FROM edges WHERE asset = 'ETH'
)
SELECT address,
COUNT(DISTINCT CASE WHEN amt_in > 0 THEN peer END) AS in_degree,
COUNT(DISTINCT CASE WHEN amt_out > 0 THEN peer END) AS out_degree,
SUM(amt_in) AS value_in, SUM(amt_out) AS value_out,
SUM(amt_out) / NULLIF(SUM(amt_in), 0) AS pass_through,
MIN(ts) AS first_seen, MAX(ts) AS last_seen
FROM io
WHERE ts >= TIMESTAMP '2026-03-01' AND ts < TIMESTAMP '2026-04-01'
GROUP BY address
HAVING COUNT(DISTINCT CASE WHEN amt_out > 0 THEN peer END) >= 20;
The CTE turns every edge into a receiver row and a sender row, so one GROUP BY yields both directions; NULLIF avoids division by zero, and HAVING keeps strong fan-out nodes. The window sees only activity inside it: an address funded in February shows a pass-through ratio far above 1 in March.
Centrality
Centrality measures how important a node is to the structure of the graph, not just how busy it is.
| Metric | What it measures | Typical use |
|---|---|---|
| Degree centrality | Number of direct connections | Finding hubs and services |
| Betweenness centrality | How often the node lies on shortest paths between other nodes | Finding brokers and bottlenecks between otherwise separate groups |
| PageRank | Importance inherited from important neighbors | Ranking nodes in large flow graphs |
In an investigation subgraph, an address with high betweenness but modest degree is often the most interesting node: the collector every dispersal path returns to, or the single bridge between a theft cluster and its cash-out exchanges. Compute centrality on a bounded subgraph with services as end points; on the full chain, exchanges and popular contracts dominate every ranking. Graph libraries compute these metrics; plain SQL handles degree but not betweenness.
Patterns: fan-out, fan-in and peel chains
Fan-out is one node sending to many in a short period; fan-in is many sending to one. Both are normal (batched withdrawals, sweeps, mining payouts, airdrops); the questions are who the parties are and what happens next:
- Dispersal: a theft address fans out to dozens of fresh addresses of similar value with no prior history. Similar amounts and fresh recipients distinguish this from a business payout.
- Collection: many scam victims pay one address (fan-in), which forwards the total onward.
- Consolidation: in UTXO chains, a fan-in transaction that spends many outputs together is also clustering evidence (Lesson 4.1).
A peel chain, described in the 2013 study "A Fistful of Bitcoins" by Meiklejohn and colleagues, starts with a large amount at one address. Each transaction peels off a small payment and sends the large remainder to a new address, which repeats the process. With a fee of 0.01 BTC per step:
| Step | Input | Peeled off | Remainder (change) |
|---|---|---|---|
| T1 | 50.00 | 1.20 → exchange deposit | 48.79 |
| T2 | 48.79 | 0.80 → exchange deposit | 47.98 |
| T3 | 47.98 | 2.50 → OTC broker | 45.47 |
| T4 | 45.47 | 0.95 → exchange deposit | 44.51 |
Signs: one input and two outputs per transaction; one output much larger and soon spent in a transaction of the same structure; the same script type and wallet fingerprints; hops minutes or blocks apart. A 2023 study by Gong, Chow, Yiu and Ting ("Analyzing the Peeling Chain Patterns on the Bitcoin Blockchain") found that about 71% of the transactions in the chains it identified peeled off less than 1% of the input, and about 79% of chains were created within a day. The remainder carries the bulk of the value, so the analyst follows it and logs each peel as a cash-out point. The shape is not inherently illicit: a wallet paying bills from one large balance, or a hot wallet paying withdrawals one by one, looks the same.
Layering, cycles and motifs
Layering, the laundering stage that distances funds from their origin, appears on chain as long chains of short-lived fresh addresses, rapid automated hops, splits and re-merges, asset swaps and chain moves. No single hop is suspicious; the combination is. Round-tripping is value that returns to its node, or to another node of the same party: a cycle. Short cycles can inflate the trading volume of a token or NFT (wash trading), fake a payment history or recycle funds through a service. Cycle detection works best on an entity graph, because a party returning funds to a different address of its own forms a cycle only after clustering.
A motif is a small subgraph shape that recurs more often than chance predicts. A widely used synthetic dataset for training anti-money-laundering models (Altman and colleagues, NeurIPS 2023) generates eight: fan-out, fan-in, gather-scatter, scatter-gather, cycle, bipartite, stack and random. In scatter-gather, one source splits to intermediaries that all forward to one destination, adding hops without changing ownership; gather-scatter is many sources converging on one node that disperses the total, typical of a scam collection address; a stack is two fully connected layers of intermediaries. Motif searches are graph-database pattern queries or time-constrained self-joins on the edge list. The time window is the key parameter: scatter-gather within one hour is a strong signal; over a year it is everyday commerce.
Signals, not conclusions
Edges are recorded facts; roles, patterns and motifs are interpretations with innocent explanations. Treat each as a reason to look closer, combine independent signals before drawing an inference, and record the thresholds used (degree cut-offs, time windows, amount tolerances) so that another analyst can reproduce and challenge them.
Exam focus: the difference between transaction, address and entity graphs and what each is good for; why the UTXO spending graph is exact while the address graph needs an allocation assumption; computing proportional edge weights for a multi-input, multi-output transaction; the attributes every edge needs and why parallel edges keep their timestamps; the three sources of account-model edges and why reverted ones are excluded; bounding a graph and treating supernodes as stopping points; degree, pass-through ratio and holding time as role indicators; betweenness on a bounded subgraph; fan-in, fan-out, peel chains (and why the remainder is followed), cycles and scatter-gather motifs as signals that need corroboration and documented thresholds.
Domande di esercitazione
Verifica la tua comprensione. Queste domande non contano per l’esame.
Accedi per salvare i tuoi progressi e riprendere da dove hai lasciato. È gratuito. Accedi