PATINA docs

Marker grammar

A marker is one OP_RETURN output with exactly one minimal data push. Anything else is not a marker.

What you will know after this page
  • How to tell a marker from any other OP_RETURN: one minimal push, the four magic bytes, a version byte, an opcode byte.
  • Which output counts as the marker when a transaction has several, and why two payloads starting with PTNA void both.
  • The 18 byte SEED payload field by field, and the 26 byte script it turns into.
  • How to lay out a KEEP payload of 1 to 8 two byte entries and read the byte count back.
  • The order to parse in, and why a transaction with no marker at all is not an error.

The shape

Every marker opens with the same six bytes. The table names them, and only the payload after them changes with the opcode. The public protocol page draws the same output if you want the picture first.

OP_RETURN PUSH(n) [ "PTNA" | version(1) | op(1) | payload ]
Fixed header, present in every marker.
FieldSizeValueNotes
magic4 bytes50 54 4e 41ASCII PTNA
version1 byte01Anything else is MARKER_UNKNOWN_VERSION
op1 byte01 SEED, 02 KEEPAnything else is MARKER_UNKNOWN_OP
payloadvariesset by the opcodeSee the two tables below

Rules that make a marker valid

  1. The push must be a single minimal data push. Two pushes, a non minimal push, or any extra opcode after the push makes the marker invalid.
  2. The whole scriptPubKey must stay at or below 83 bytes. Over that is MARKER_TOO_LARGE.
  3. The protocol marker of a transaction is the OP_RETURN output with the lowest vout index whose payload starts with PTNA.
  4. If more than one output has a payload starting with PTNA, the marker is void: VOID_DUPLICATE_MARKER, and the default rule applies as if there had been no marker.
Why minimal pushes matter

Bitcoin can express the same data with several encodings. If a parser accepted more than one, two implementations could disagree about whether the same transaction contained a marker. Requiring the minimal encoding leaves exactly one valid way to write it.

SEED, opcode 0x01

Creates an artifact. Payload is 18 bytes and the total push is 24 bytes.

SEED payload layout.
Offset in payloadFieldSizeMeaning
0salt16 bytesThe salt used in the commitment
16flags1 byteReserved at version 1. Every bit must be zero.
17carrier_vout1 byteIndex of the output that becomes the carrier
payload       18 bytes  2fe862993a92197e084e4070cc8aa1c30001
push data     24 bytes  50544e4101012fe862993a92197e084e4070cc8aa1c30001
scriptPubKey  26 bytes  6a1850544e4101012fe862993a92197e084e4070cc8aa1c30001
The same 26 bytes, split.
BytesMeaning
6aOP_RETURN
18Minimal push of 24 bytes
50544e41magic PTNA
01version
01op SEED
2fe862993a92197e084e4070cc8aa1c3salt
00flags
01carrier_vout

26 bytes leaves 57 bytes of headroom under the 83 byte ceiling. Validity checks for SEED are on SEED rules.

About the flags byte

The baseline reserves one byte here and assigns no meanings to it, so every bit of it must be zero at version 1. A payload with a non zero flags byte is SEED_BAD_GRAMMAR and creates nothing. If a meaning is ever assigned it arrives with a different version byte, through the process on The upgrade boundary.

KEEP, opcode 0x02

Routes artifacts when a carrier is spent. Payload is a count followed by that many two byte entries.

KEEP payload layout.
Offset in payloadFieldSizeMeaning
0count1 byte1 to 8 entries. Zero or more than 8 is KEEP_BAD_GRAMMAR.
1input_index1 byteWhich input of this transaction spends a carrier
2vout1 byteWhich output that artifact should follow
3 ...more entries2 bytes eachRepeat to count
payload       3 bytes   010002
push data     9 bytes   50544e410102010002
scriptPubKey  11 bytes  6a0950544e410102010002
payload       5 bytes   0200010203
push data     11 bytes  50544e4101020200010203
scriptPubKey  13 bytes  6a0b50544e4101020200010203
payload       17 bytes  0800010102020303040405050606070708
push data     23 bytes  50544e4101020800010102020303040405050606070708
scriptPubKey  25 bytes  6a1750544e4101020800010102020303040405050606070708

Even at the maximum of eight entries the script is 25 bytes, 58 under the ceiling. The size limit is not the reason for the eight entry cap. Rules for each entry are on KEEP and the default rule.

Marker size, at a glance

Every marker the protocol can produce, with the room it leaves under the 83 byte ceiling. The point of the table is the last column: nothing legal comes anywhere near the limit, so a MARKER_TOO_LARGE is a construction bug rather than a tight fit.

Script sizes for every legal marker.
MarkerPayloadPushscriptPubKeyHeadroom to 83
SEED18242657
KEEP, 1 entry391172
KEEP, 2 entries5111370
KEEP, 8 entries17232558

Parsing order for an implementation

Apply these in the order written. A parser that checks the opcode before the version records a different reason code from the one the specification names, on exactly the same bytes.

  1. Walk the outputs in index order. Collect every OP_RETURN whose data push starts with the four magic bytes.
  2. If there is more than one, record VOID_DUPLICATE_MARKER and stop treating the transaction as marked. The default rule still applies to any carrier the transaction spends.
  3. If there is exactly one, check the push is single and minimal, and the script is at or under 83 bytes.
  4. Check the version byte, then the opcode byte.
  5. Decode the payload for that opcode. A payload of the wrong length is bad grammar for that opcode.
  6. Apply the opcode rules. A marker that decodes but fails a rule records an invalid event and creates nothing.

An unmarked transaction is not an error. Most transactions that spend a carrier carry no marker at all, and the default rule handles them.

To read a marker out of a real transaction rather than a table, the public verify walkthrough pulls the same bytes off a raw hex dump.