PATINA docs

State machine

Two states, three transitions, and one record. Everything else is derived at query time.

What you will know after this page
  • Which fields are stored, and why depth and tier are not among them.
  • What each of the three transitions writes: the ring appended, the carrier set, the status changed.
  • Why RELIC is terminal, and what a relic keeps forever anyway.
  • The order state is built in: blocks by height, transactions in block order, nothing from the mempool.
  • Why artifacts sharing one carrier cannot be separated by a single transaction.

The artifact record

One artifact record, with its rings nested inside it, and that is the whole model. Anything a client shows you is either a field below or a subtraction from one. The public depth page takes the same record apart field by field, with the changing parts moving.

Artifact {
  artifact_id
  birth_txid, birth_height, birth_vout
  endowment_sats
  founding
  status: ALIVE | RELIC
  carrier: { txid, vout, height, value } | null
  rings: Ring[]
}

Ring {
  index
  start_height, end_height
  depth
  carried_value
  successor_txid | null
  successor_vout | null
  relic
}

Depth and tier are not in the record. They are computed when you ask, from the carrier height and the current tip. Nothing is written per block.

The two states

The whole difference is whether a carrier exists. Every row in the table follows from that one fact.

ALIVE and RELIC.
ALIVERELIC
carrierAn unspent outpointnull
depthtip_height - carrier.heightNot applicable
Can gain ringsYes, one each time the carrier is spentNo
Can return to the other stateBecomes RELIC when a spend has no successorNever. Terminal.
Rings readableYesYes, forever

The three transitions

Birth: nothing to ALIVE

A valid SEED transaction creates the artifact. carrier is set to the named output, at the height of the block containing the reveal. rings is empty. Depth starts at zero and increments with every block that passes.

Conditions on SEED rules.

Move: ALIVE to ALIVE

A confirmed spend of the carrier ends the stretch.

  1. Append a ring with start_height = carrier.height, end_height = spend_height, and depth = spend_height - carrier.height.
  2. Record carried_value as the value the carrier held.
  3. Choose the successor with a valid KEEP entry, or the default rule.
  4. Set carrier to the successor outpoint, with the spend block's height and the successor's value.
  5. Record successor_txid and successor_vout on the ring.

Depth is now zero again, counting from the block that made the successor.

End: ALIVE to RELIC

Same spend, but no eligible successor exists: no valid KEEP entry, and no output that is both non OP_RETURN and at or above 10000 sats.

  1. Append a ring the same way, with successor_txid and successor_vout null and relic: true.
  2. Set carrier to null and status to RELIC.

There is no path out. A relic keeps its id, its rings, and its founding flag forever.

What does not change, ever

  • artifact_id
  • birth_txid, birth_height, birth_vout
  • founding
  • Any ring that has already been appended

The only exception is a reorg, which does not change these values so much as unwind the blocks that produced them. See Reorg behavior.

Everything happens in block order

State is a function of the blockchain and nothing else. Given the same chain, every correct implementation produces the same state.

  1. Process blocks in height order.
  2. Inside a block, process transactions in the order they appear in the block.
  3. Inside a transaction, the spend of an existing carrier and the creation of a new artifact are independent. Whether a spent input was a carrier is decided by the state before this transaction, and a newly created carrier is an output of this transaction, so it cannot also be spent by it.
  4. Nothing in the mempool affects state. An unconfirmed spend has not happened.

Bundles: several artifacts, one carrier

Successor routing can land two artifacts on the same output. From then on they share a carrier and move together. The public risks page states the same thing as a warning, because it surprises people at the moment they try to sell one artifact out of a group.

  • All artifacts on a shared carrier have the same depth, because depth is a property of the outpoint.
  • Spending that carrier ends every one of their stretches at once and engraves a ring on each.
  • A KEEP entry names an input, and one input can name only one vout. So one transaction routes every artifact on that carrier to the same place.
Splitting a bundle takes several transactions

Because a single spend of a carrier moves everything on it to one destination, you cannot split a bundle in one transaction. You split it across a sequence: move the bundle, then move the resulting carrier again, arranging along the way for the artifacts you want apart to end up on different carriers. Each step is a spend, so each step resets depth for everything it touches. Plan it before you need it, not during a sale.

Invalid events are part of the state

A transaction that carries a marker and fails a rule does not change any artifact, but it does record an invalid event with a reason code. Those events are served by GET /patina/invalid-events, and they are what makes a failed mint diagnosable instead of mysterious.

Treat them as part of the state you must reproduce. Two implementations that agree on artifacts but disagree on invalid events do not agree. See Reason codes.