The Pile
non-monetary data on Bitcoin, measured from a full node
Inscriptions
A reveal transaction exists for one reason. Nobody builds one to move money, so every byte of it is caused by the inscription — and a node stores transactions, not envelopes. The payload is 56% of a reveal; the signatures and skeleton are the other 44%.
- Counted
- The whole reveal transaction — signature, skeleton, dust output and change alike
- On a transfer, only the inscribed output being spent and the inscribed output it becomes
- Not counted
- The commit transaction. Every inscription needs one, but it carries no envelope and nothing identifies it. The largest known omission on this page
- The rest of a transfer — fee input, change, skeleton. Real when the transaction moves an inscription, not real when a wallet sweeps dust that happens to include one, and nothing in the protocol tells those apart
- Any OP_RETURN inside a reveal. Deducted here and charged below instead
OP_RETURN
An OP_RETURN output is provably unspendable — coins sent to one are destroyed. So none of it does monetary work, at any size, however old the rule that permitted it. Storage does not care which rule let the bytes in.
- Counted
- Every OP_RETURN output on the chain, at the full size a node stores it: script, value field and length prefix
- Not counted
- The transaction carrying it. Unlike a reveal, it is usually settling a payment too — only the data-carrying output is attributable
- The coinbase OP_RETURN. That is the SegWit commitment, protocol machinery rather than carried data
- Script-only size is tracked separately, because the old 83-byte rule governed the script rather than the output
Chainstate
Every inscription rides on a real output, because the numbering convention needs a coin to pin data to. Below roughly a thousand satoshis that output costs more to spend than it holds, so it never moves — and no node can prune it.
- Counted
- Reveal outputs at or below 1,000 satoshis that are still unspent
- Not counted
- Change returned to whoever made the inscription. That is ordinary money, and spending it later is ordinary spending
- Anything that inherited the tag by being passed along. An output descended from an inscription is not itself one
- Both wider counts are measured as a cross-check and a ceiling. The published figure is the smallest of the three
Change is excluded here and counted above. That is not a contradiction — the two categories ask different questions of the same output. Was this byte written because of the inscription? Yes: the transaction exists for no other reason, so all of it counts. Is this coin an inscription burden on every node forever? No: it is money, and it will be spent like money.
Nothing is counted twice. An inscription transaction is counted once — one that both reveals and moves is a reveal, not both — and its serialised size already contains the envelope, the outputs and any OP_RETURN it carries, so those are never added again. OP_RETURN is counted only outside those transactions. Chainstate cannot overlap either, because it is a different database: a node genuinely stores the same output twice, once in block history as a record of what happened and once in the chainstate as an index of what exists now. Counting both is not double counting — it is what the node holds.
Inscriptions — an accident, then a market
An inscription is a file — an image, a line of text, a token ticket — hidden inside the part of a transaction that normally holds signatures. It rides in a script branch that opens on a condition that is always false, so the interpreter skips the whole branch without ever reading it. It cannot affect whether the payment is valid, which leaves those bytes exactly one purpose: being stored.
OP_FALSE OP_IF <data> OP_ENDIF
None of this was designed. SegWit in 2017 moved signatures into a separate witness area and priced those bytes at roughly a quarter of the normal rate — a fix for transaction malleability, with cheap storage as a side effect. Taproot in 2021 then removed the limit on how large a single script could be, so that complex spending conditions would fit — and, unintended, so would a script the size of an entire block. Two upgrades, each reasonable alone, combined into a large discounted slot guarded only by the rule that a branch which never runs is never examined.
In December 2022 a developer demonstrated the gap by using it; the first inscription sits in block 767,430. Within weeks the technique shipped as a public tool with a convention that assigns each inscription to an individual satoshi, giving the data an owner — NFTs on Bitcoin. Text-token mints followed in 2023 and came to dominate by count. It was raised with Bitcoin Core as a gap in the data limits; the maintainers declined to filter it, on the reasoning that a relay filter does not bind miners and would mostly push the traffic out of view.
The point that matters for measurement: inscriptions are not a feature that was switched on. They are a seam between two upgrades — found, then productised. There was never a rule against them to relax.
OP_RETURN — the deliberate one
OP_RETURN is the opposite story: a data channel added on purpose. By 2014 people were already embedding data in fake addresses, which forces every node to carry those unspendable "coins" in its working memory forever. Bitcoin Core 0.9 offered a lesser evil — an output type that is provably unspendable, so a node stores it once in block history and never adds it to the list of live coins. It launched at 40 bytes of payload, settled at 80, and stayed there for a decade.
In October 2025 Bitcoin Core v30 removed that default cap: multiple OP_RETURN outputs per transaction, up to roughly 100 KB. That is the Core v30 marker on the chart above.
Every node still downloads, verifies and stores every byte of it, exactly like inscription data. What it avoids is the second, permanent cost — it never enters the chainstate. That difference is the entire reason the third category exists.
Chainstate — the permanent residue
The chainstate is the database of every coin that currently exists, which every node keeps on hand to check whether the next transaction is spending something real. Inscription dust enters it and stays: spending a few hundred satoshis costs more in fees than it recovers, so tens of millions of these outputs now sit in every node's working set.
This is data doing precisely what OP_RETURN was invented in 2014 to prevent.
It is also different in kind from everything above. Blocks are history: a pruned node deletes them and carries on validating. The chainstate is state — no setting removes an entry while the coin exists. So the smallest number on this page is the only permanent one, and it is the one that decides whether modest hardware can still run a node in ten years.
Reading the witness
Each witness script is walked token by token — Bitcoin Script
is a fully specified grammar, so every byte belongs to exactly
one token — and OP_FALSE OP_IF is matched to its
OP_ENDIF by depth, so a conditional nested inside
an envelope cannot close it early. An envelope with no matching
OP_ENDIF is not counted at all.
Detection is structural, not a list of protocols. The shape of the script is the whole test, so the same rule that caught the first inscription in 2022 catches whatever is invented next, with no code change and no guess at intent.
Every witness byte then lands in exactly one bucket, and the sum is asserted on every single input:
envelope + overhead + residual = total witness bytes
The residual is everything the parser could not
attribute, published alongside the headline so a novel embedding
trick appears as a spike rather than vanishing. Each block's
totals are also checked against size − strippedsize,
a figure the node reports independently and the parser never
touches.
Reading the outputs
Every output in every block is examined. An OP_RETURN announces itself in its first byte, so finding them needs no heuristics and no protocol list — the script either starts that way or it does not.
Each one is measured twice, because two different questions are being asked of it. The stored size is what a node keeps on disk, and feeds the total above. The script size is what the pre-v30 relay rule capped at 83 bytes, and feeds the per-block grades below. Mixing them would compare a storage figure against a policy limit that never governed storage.
Bytes past that allowance are tracked as excess, and because v30 relaxed two rules at once — the size cap and the one-output limit — a transaction breaking only the count rule is recorded separately from one carrying oversized data.
Tracking the chainstate
The tracker walks the chain strictly in order, starting thirty blocks before the first inscription — so the tagged set begins genuinely empty, with nothing inherited and nothing estimated. Outputs are tagged as they are created and untagged the moment the chain spends them. Order matters within a block too: a transaction can spend an output an earlier transaction in the same block created.
Each entry is stored as the real outpoint rather than a hash, so any of them can be handed straight back to the node and confirmed unspent — the bookkeeping is auditable line by line, and no collision argument is needed anywhere.
Size uses the node's own database-independent metric
(bogosize = 50 + script length), converted to real
bytes with the node's own disk_size / bogosize
ratio, so every term in the conversion came off the node. As an
outside check, counting every reveal output reproduces the
figure mempool.space published in its UTXO Set Report —
51,188,145 at block 892,385 — from entirely separate code.
Two measures, on purpose
The total above is a storage measure: what a node carries because this data exists. It answers what the pile costs.
The live block section answers a different question and uses a narrower measure. Each block there is graded on what the pre-2023 defaults would not have carried — the inscription payload alone, plus only the OP_RETURN beyond the old 83-byte allowance. Signatures are left out because any transaction needs them, and ordinary OP_RETURN is left out because it was a deliberate, uncontroversial channel; including it would lay a constant background across every block and hide the thing worth seeing.
So the percentages on the blocks, the colour bands and the clean-block count all use that narrower measure. Nothing else on the page does, and the two are never added together.
The bounds, stated
Two figures on this page are ranges rather than points, and both are published at the low end.
Transfers. Only the provable parts are counted, which undercounts a genuine transfer and refuses to overcount a sweep. The whole-transaction figure is measured as an upper bound, so the answer to "how much is this missing?" is a number rather than a shrug.
Chainstate. The published count excludes change and excludes everything the tag reached by propagation. Counting every reveal output gives the cross-check; counting everything tagged gives the ceiling.
What this does not measure
"Would have blocked" is softer than it sounds. The OP_RETURN cap was a relay default, and miners taking transactions directly could always ignore it. Inscriptions were never refused by Bitcoin Core at all. Neither would have been impossible — both would have been harder or dearer.
Data can move. Witness bytes are cheaper than output bytes, so data blocked from one route may take another rather than disappear.
Some carriers are not detected. Protocols that hide data in fake public keys are counted here as ordinary spending — and their outputs sit in the chainstate too. A pipeline for them is planned.
The total is a floor. Commit transactions are missing, fake-key carriers are missing, transfers and chainstate are both published at their low bound, and blocks between the last full scan and the live feed are not counted yet. Every gap points the same way.
Every block since the first inscription is parsed on a full node — no indexers, no third-party data, no sampling. A gap in the block range is an error rather than a mode. The classifiers are covered by tests that run without a node, every witness byte is bound by a per-input accounting identity, and every block is checked against a measure the node reports independently. Source, tests and the full method: github.com/bretstu/thepile.