Implementer reference
Reference
Terminology, the indexer's view of the chain, and the decisions an implementation has to get right. Grounded in ord 0.29.0: src/index/updater/rune_updater.rs, src/index/entry.rs, src/index/reorg.rs, src/lib.rs, and the ordinals crate.
Terminology
- Runestone
- The protocol message: an OP_RETURN output beginning
OP_RETURN OP_13, whose data pushes concatenate into a payload of varints. - Artifact
- The result of deciphering a transaction. Either a well-formed runestone or a cenotaph. A transaction with no matching output has no artifact at all, which is different from having an empty one.
- Cenotaph
- A malformed runestone. Burns input runes, makes an etched rune unmintable, and burns mint output while still counting the mint against the cap.
- Flaw
- The named reason a runestone is a cenotaph. Ten exist; the reference implementation reports the first one found.
- Etching
- The act of creating a rune and permanently fixing its name, divisibility, symbol, spacers, premine, and terms.
- Rune ID
BLOCK:TX: the height of the etching block and the etching transaction's index within it. Assigned once, never reused.- Edict
- An instruction inside a runestone: allocate an amount of one rune to one output.
- Pointer
- The output index that receives unallocated runes, overriding the first non-OP_RETURN output default.
- Premine
- Units allocated to the etching transaction itself, outside the mint.
- Terms
- The conditions of an open mint: amount per mint, cap, and height or offset windows.
- Turbo
- A flag on the etching declaring that the rune opts in to future protocol changes. It carries no meaning under the current rules.
- Atomic unit
- The indivisible unit of a rune. Display value is the atomic amount shifted by the rune's divisibility.
- Unallocated pool
- Per transaction: input balances, plus a successful mint, plus a premine. Edicts draw from it; the remainder follows the pointer.
- Reserved name
- A name at or above
6402364363415443603228541259936211926. Unetchable directly; assigned by the indexer to etchings that omit a name. - Commitment
- The tapscript data push in an input's witness proving the etcher claimed the name at least 6 blocks earlier.
Indexer pipeline
0:0.Two consequences worth internalizing. First, the cenotaph check happens after allocation is computed, and it discards all of it: allocations are replaced wholesale by burns of the entire unallocated pool. Second, an unsuccessful mint is invisible. It leaves no trace in the index, no event, and no error, and it never counts against the cap unless the transaction is a cenotaph for some other reason.
Terms evaluation
A mint at height h succeeds when the rune exists, has terms, and all of the following hold:
- h is not below start, where start is the larger of
HeightStartandetching block + OffsetStart. If only one is present, it alone applies. If neither is present, there is no lower bound. - h is below end, where end is the smaller of
HeightEndandetching block + OffsetEnd. Same fallback rules. The end height is exclusive: minting at h equal to end fails. - the recorded mint count is below the cap. An absent cap is treated as 0, so terms without a cap are unmintable from the outset.
Because start takes the maximum and end takes the minimum, giving both an absolute height and an offset narrows the window rather than widening it. Amount is taken as 0 when absent, which produces successful mints that create nothing while still consuming cap.
Supply accounting: supply = premine + mints × amount and max supply = premine + cap × amount. Burned units are tracked separately and are not deducted from supply.
Confirmation and mempool
- Confirmed blocks are the only truth. Rune state is a function of confirmed transactions in block order. The reference indexer processes runes only from the activation height onward, so blocks below 840,000 on mainnet are skipped entirely.
- Transaction order inside a block is significant. Rune IDs use the transaction's index within its block, and a mint's success depends on the mint count at that moment. A transaction can mint a rune etched earlier in the same block.
- There is no mempool consensus. An unconfirmed runestone predicts nothing reliably: the rune's mint count can change, a competing etching of the same name can confirm first, and fee bumping can replace the transaction outright. Show unconfirmed rune activity as provisional or not at all.
- Etching has a 6-block prerequisite. A commitment output must have at least 6 confirmations relative to the etching block, which is a protocol rule, not a display convention.
- Bitcoin Universe's own runes feed reports partial coverage precisely because it does not carry an exhaustive rune mempool stream with a stable pending lifecycle. Confirmed history and holder state are indexed; pending activity is not promised.
Reorg behavior
Rune state is fully derived from the chain, so a reorg is handled by rewinding rather than by patching. The reference indexer keeps periodic database savepoints (by default one every 10 blocks, retaining 2) and, on detecting that a stored block hash no longer matches the chain, restores the oldest savepoint and re-indexes forward. A reorg deeper than the retained savepoints is reported as unrecoverable and requires re-indexing.
Practical consequences for anything built on rune data:
- Rune IDs are not stable across a reorg. If the etching transaction moves to a different block or index, the ID changes. Treat an ID as final only after enough confirmations for your risk tolerance.
- Mint counts, and therefore whether a mint is open, can move backwards.
- Balances are keyed by outpoint. A reorg that invalidates a transaction invalidates every balance it created.
- Cache invalidation must be driven by block hash, not block height.
Sizes and fees
Runestones are small. These are exact script sizes in bytes from the reference implementation's own size assertions:
| Runestone | Size |
|---|---|
Empty (OP_RETURN OP_13 only) | 2 |
| Etching, shortest name, no other fields | 7 |
| Etching, longest name, no other fields | 25 |
| Etching with every field at maximum | 89 |
| One edict, amount 0 | 14 |
| One edict, maximum amount | 32 |
| Two edicts, maximum amounts | 54 |
| Three edicts, maximum amounts | 76 |
| Five edicts, u64-sized amounts | 75 |
Relay policy, not the protocol, sets the practical ceiling. The reference wallet refuses to build a runestone whose script exceeds 83 bytes, the standard OP_RETURN size it compiles in, unless explicitly overridden, and it enforces the 400,000 weight unit standard transaction limit. Neither is a consensus rule and neither is part of the Runes specification: a miner-included runestone above those sizes is valid and will be indexed. The payload is emitted as a single data push in practice, since chunking only begins above 4 GiB.
The dominant cost is rarely the runestone. It is the outputs: every recipient of a rune balance needs a spendable output above the dust threshold, so a transfer to n recipients costs n outputs plus change plus the OP_RETURN. Sweeps (amount 0) and splits (output index equal to the output count) exist to keep the runestone itself short.
Limitations
- Names are permanently unique and first-come. There is no renewal, transfer, or revocation of a name.
- Etched properties are immutable. There is no upgrade path for a rune's divisibility, symbol, or terms, and no admin key.
- One runestone per transaction. Multiple protocol messages require multiple transactions.
- Amounts are u128 atomic units. Divisibility above 38 is rejected, so the displayed precision is bounded.
- No conditional logic, no scripting, no royalties, no freeze or clawback. Runes are transfers of quantities and nothing else.
- The turbo flag is declarative only. It does nothing today.
- Runes and inscriptions can share an output. An inscription-aware wallet that ignores rune balances, or a rune-aware wallet that ignores inscriptions, will eventually destroy something. The org's own ordinals marketplace path requires a listed output to carry no co-located runes for exactly this reason.
Security considerations
- Cenotaphs burn silently and totally. One malformed integer burns every rune in the transaction's inputs, not just the amount you were sending. Validate the encoded runestone by decoding it before signing, not by trusting the encoder.
- The default destination is a footgun. Unallocated runes go to the first non-OP_RETURN output. Any transaction that spends a rune-bearing output without an explicit plan for it sends the balance to whoever holds output 0.
- Rune-unaware coin selection destroys value. A wallet that treats rune outputs as ordinary UTXOs will spend them as fee inputs. Maintain a rune outpoint inventory and select only cardinal (rune-free, inscription-free) inputs for fees.
- Names are confusable. Spacers are display-only, so
BITCOIN•UNIVERSEandBIT•COINUNIVERSEcannot both exist, but visually similar distinct names can. Identify runes by ID, not by name, in anything security-relevant. - Etching is racy. Between your commit and your reveal, six blocks pass in which someone else can etch the name. Handle the failure as an expected outcome.
- Mints can fail without failing. A closed mint produces a valid, confirmed, fee-paying transaction that creates nothing. Check terms against the current height and mint count at build time and warn on the margin.
- Indexer disagreement is a settlement risk. Two indexers that differ on a single edict differ on ownership. Before treating rune data as authoritative for value transfer, verify it against a second independent source, and require the chain tip and the indexer tip to agree.
- Do not blind-sign. Decode the OP_RETURN, check the edict table, check output 0, check the pointer.
Implementation checklist
- Search outputs in order for
OP_RETURN OP_13. Take the first match; skip unparseable and non-matching outputs without erroring. - Concatenate all data pushes. Treat any non-pushdata opcode as flaw
Opcodeand any unparseable script asInvalidScript. - Decode the whole payload as LEB128 varints with the 19-byte limit and the 19th-byte overflow mask. Any failure is flaw
Varint. - Parse tag and value pairs until tag 0; then parse edicts in groups of four. Handle
TruncatedFieldandTrailingIntegers. - Accumulate edict rune IDs by delta, rejecting overflow and block 0 with nonzero tx (
EdictRuneId), and range-check outputs against the output count, allowing equality as a split (EdictOutput). - Consume flags and fields in the reference implementation's order, leaving values in place when a range check fails, so that odd tags degrade and even tags become
UnrecognizedEvenTag. - Check
premine + cap × amountfor u128 overflow (SupplyOverflow) and confirm no flag bits remain set (UnrecognizedFlag). - Enforce the etching preconditions: minimum name at height, not reserved, not already etched ignoring spacers, and a valid taproot tapscript commitment with at least 6 confirmations.
- Apply the pipeline in order: unallocated, mint, etch and premine, edicts, pointer remainder, burns. Clamp every allocation to the remaining balance.
- Burn everything on a cenotaph, count the mint, and record the etched rune as unmintable with default properties.
- Persist balances per outpoint and per rune, and derive account views from them. Never treat an address as the unit of ownership.
- Handle reorgs by rewinding to a savepoint and re-indexing. Key caches by block hash.
- Verify against the test vectors, including every cenotaph case, before indexing real value.