Building transaction graphs · Lesson 2 of 6
Tracing funds forward and backward
8 min read
Tracing is the core skill of a blockchain analyst: following value from a known point to where it went, or back to where it came from. The hard part is not following edges, which any explorer can do, but deciding how much of the value on each edge is "the funds you care about" once they mix with other money. Different allocation rules give very different answers from the same data, so you must know them, choose one deliberately and say which one you used.
Forward and backward tracing
Forward tracing starts at a known source, such as a theft address, a ransom payment or a sanctioned wallet, and follows outgoing edges to find where the funds went: which services received them, which addresses still hold them, and whether any reached a point where they can be frozen or explained.
Backward tracing starts at a destination, typically an incoming deposit at an exchange, and follows incoming edges to answer "where did this money come from?". Exchange risk teams do this for source-of-funds checks: a deposit that arrives one hop from a darknet market is a different risk from one that arrives ten hops away through a large exchange.
Both directions measure distance in hops, the number of transfers between two points. Direct exposure means the counterparty is one hop away; indirect exposure means the link runs through intermediate addresses. Hop counts are only meaningful in a graph you have defined carefully (see the lesson Transaction graphs: nodes, edges and direction): one hop in an entity graph may be ten in an address graph.
Taint and stopping rules
Taint is the share of a balance or a transfer that the analyst attributes to the source of interest. Taint is not a property of the coins; it is the result of a rule you apply. Every trace also needs stopping rules, decided in advance:
- Service reached: the funds enter an exchange, payment processor or other custodial service, where on-chain tracing ends (see the lesson Following value through exchanges and services).
- Dilution threshold: the tainted share falls below a set level, for example 1%.
- Hop limit: a maximum distance, for example 3 to 6 hops, beyond which links are too weak to report.
- Value floor: amounts too small to matter, such as dust below a fixed fraction of the original sum.
- Time window: transfers that happened before the funds arrived cannot carry them.
The last rule is easy to forget in SQL. An address that received stolen funds on 10 March and paid someone on 2 March did not pay them with stolen funds.
Four allocation methods
When tainted and clean value meet in one address or one transaction, a rule decides which outgoing value carries the taint.
| Method | Rule | Assumption | Effect |
|---|---|---|---|
| FIFO (first in, first out) | Outflows are drawn from the oldest inflows first | Funds behave like a queue | Taint stays concentrated in specific outflows |
| LIFO (last in, first out) | Outflows are drawn from the newest inflows first | Funds behave like a stack | Taint moves on quickly if spent soon after arrival |
| Haircut (pro rata) | Each outflow carries the tainted share of the balance at that moment | Funds mix perfectly | Taint spreads thinly across every later outflow |
| Poison | Any outflow from a balance that holds any taint is fully tainted | Any contact contaminates | Taint grows to cover far more than the original amount |
Poison and haircut were named in an early academic study of risk scoring for Bitcoin (Möser, Böhme and Breuker, "Towards Risk Scoring of Bitcoin Transactions", 2014). FIFO borrows a long-standing accounting convention for mixed bank accounts; Anderson, Shumailov, Ahmed and Rietmann at Cambridge argued in "Bitcoin Redux" (2018) that it suits Bitcoin well because it is deterministic and does not dilute: applied inside a transaction, it orders value by input and output position, so the taint lands on specific satoshis of specific outputs. LIFO is the mirror-image ordering, offered by some tracing tools as an alternative. None of the four is "correct"; each is a model.
A worked example
An address A receives and spends funds in this order. S is 2 BTC from a theft; C1 and C2 are unrelated, clean funds.
| Step | Event | Balance after |
|---|---|---|
| 1 | +4 BTC from C1 (clean) | 4 |
| 2 | +2 BTC from theft S | 6 |
| 3 | −3 BTC to X | 3 |
| 4 | +4 BTC from C2 (clean) | 7 |
| 5 | −5 BTC to Y | 2 |
| 6 | −2 BTC to Z | 0 |
FIFO. After step 2 the queue is [4 clean, 2 stolen]. X takes 3 from the oldest lot: 3 clean. The queue is now [1 clean, 2 stolen], then C2 adds [4 clean]. Y takes 1 clean, 2 stolen and 2 clean. Z takes the last 2 clean. Result: X 0, Y 2, Z 0 BTC tainted.
LIFO. After step 2 the stack is [4 clean, 2 stolen] with the stolen lot on top. X takes the 2 stolen plus 1 clean. C2 then adds 4 clean on top. Y takes those 4 plus 1 more clean; Z takes the last 2 clean. Result: X 2, Y 0, Z 0.
Haircut. After step 2 the balance is 6 BTC, of which 2 is tainted (33.33%). X receives 3 × 2/6 = 1.000 tainted. The 3 BTC left contain 1 tainted; after C2 the balance is 7 with 1 tainted (14.29%). Y receives 5 × 1/7 = 0.714 tainted, leaving 2 BTC with 0.286 tainted, which all goes to Z. Result: X 1.000, Y 0.714, Z 0.286.
Poison. Every outflow after step 2 comes from a balance that holds taint. Result: X 3, Y 5, Z 2, so 10 BTC is labeled tainted although only 2 BTC was stolen.
| Recipient | FIFO | LIFO | Haircut | Poison |
|---|---|---|---|---|
| X (3 BTC) | 0 | 2 | 1.000 | 3 |
| Y (5 BTC) | 2 | 0 | 0.714 | 5 |
| Z (2 BTC) | 0 | 0 | 0.286 | 2 |
| Total tainted | 2 | 2 | 2.000 | 10 |
FIFO, LIFO and haircut all conserve value: the total tainted amount stays at 2 BTC. Poison does not. The methods also disagree about who received the stolen funds: FIFO says Y, LIFO says X, haircut says everyone a little. If X is an exchange where funds could be frozen and Y is a private wallet, the choice changes the outcome of the case.
What the methods do at scale
Repeat haircut over many hops and the taint spreads thinner and wider with every mix. In the same "Bitcoin Redux" study, Anderson and colleagues traced the 46,653 BTC stolen in the 2012 Linode theft: haircut tainted about 93% of the addresses in their dataset, while FIFO tainted about 1.3%. Poison spreads faster still, which makes it useful only for narrow questions, such as "has this address had any contact with a mixer?", and misleading as a measure of amount.
Practical guidance:
- Use haircut for risk scores and exposure percentages, where a proportional answer is the honest one.
- Use FIFO (or LIFO) when you must name the specific outputs that carry funds, for example to request a freeze, and say that the attribution follows the rule, not the coins.
- Use poison for binary contact flags, never for amounts.
- Run the trace under two methods when the result matters. If both point to the same service, confidence rises; if they diverge, report the range.
Expanding hops in SQL
A bounded forward trace can start as a recursive query over the edge list built in the previous lesson. Recursive CTEs are supported by most engines, but not all; on some warehouses you iterate hop by hop from a notebook instead.
WITH RECURSIVE hops AS (
SELECT dst AS address, 1 AS hop, block_number
FROM edges
WHERE src = '0xA1b2…9F' -- seed: the theft address
UNION ALL
SELECT e.dst, h.hop + 1, e.block_number
FROM hops h
JOIN edges e ON e.src = h.address
AND e.block_number >= h.block_number -- time moves forward
WHERE h.hop < 4 -- hop limit
AND h.address NOT IN (SELECT address FROM service_labels) -- stop at services
)
SELECT address, MIN(hop) AS first_hop, COUNT(*) AS paths
FROM hops
GROUP BY address;
The seed row takes every recipient of the theft address. The recursive part follows outgoing edges from each address reached, only through transfers at or after the block where the funds arrived, stops at four hops and does not expand through labeled services. This query finds reachable addresses; it does not allocate amounts. Taint allocation needs the ordered inflows and outflows of each address, which is easier in a notebook or a graph tool. Expect path counts to explode around busy addresses, which is another reason to stop at services.
Exam focus: forward vs backward tracing and their typical uses; hops and direct vs indirect exposure; stopping rules, including the rule that outflows before an inflow cannot carry it; the definitions and assumptions of FIFO, LIFO, haircut and poison, and computing each on a small example; that FIFO, LIFO and haircut conserve value while poison inflates it; how haircut dilutes taint across many addresses at scale; which method suits risk scoring, freeze requests and contact flags; reporting the method and running a second one as a check.
Practice questions
Check your understanding. These questions do not count towards the exam.
Sign in to save your progress and continue where you left off. It is free. Sign in