Marker grammar
A marker is one OP_RETURN output with exactly one minimal data push. Anything else is not a marker.
- 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
PTNAvoid 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 ]
| Field | Size | Value | Notes |
|---|---|---|---|
| magic | 4 bytes | 50 54 4e 41 | ASCII PTNA |
| version | 1 byte | 01 | Anything else is MARKER_UNKNOWN_VERSION |
| op | 1 byte | 01 SEED, 02 KEEP | Anything else is MARKER_UNKNOWN_OP |
| payload | varies | set by the opcode | See the two tables below |
Rules that make a marker valid
- 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.
- The whole scriptPubKey must stay at or below 83 bytes. Over that is
MARKER_TOO_LARGE. - The protocol marker of a transaction is the OP_RETURN output with the lowest vout index
whose payload starts with
PTNA. - 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.
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.
| Offset in payload | Field | Size | Meaning |
|---|---|---|---|
| 0 | salt | 16 bytes | The salt used in the commitment |
| 16 | flags | 1 byte | Reserved at version 1. Every bit must be zero. |
| 17 | carrier_vout | 1 byte | Index of the output that becomes the carrier |
payload 18 bytes 2fe862993a92197e084e4070cc8aa1c30001
push data 24 bytes 50544e4101012fe862993a92197e084e4070cc8aa1c30001
scriptPubKey 26 bytes 6a1850544e4101012fe862993a92197e084e4070cc8aa1c30001
| Bytes | Meaning |
|---|---|
6a | OP_RETURN |
18 | Minimal push of 24 bytes |
50544e41 | magic PTNA |
01 | version |
01 | op SEED |
2fe862993a92197e084e4070cc8aa1c3 | salt |
00 | flags |
01 | carrier_vout |
26 bytes leaves 57 bytes of headroom under the 83 byte ceiling. Validity checks for SEED are on SEED rules.
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.
| Offset in payload | Field | Size | Meaning |
|---|---|---|---|
| 0 | count | 1 byte | 1 to 8 entries. Zero or more than 8 is KEEP_BAD_GRAMMAR. |
| 1 | input_index | 1 byte | Which input of this transaction spends a carrier |
| 2 | vout | 1 byte | Which output that artifact should follow |
| 3 ... | more entries | 2 bytes each | Repeat 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.
| Marker | Payload | Push | scriptPubKey | Headroom to 83 |
|---|---|---|---|---|
| SEED | 18 | 24 | 26 | 57 |
| KEEP, 1 entry | 3 | 9 | 11 | 72 |
| KEEP, 2 entries | 5 | 11 | 13 | 70 |
| KEEP, 8 entries | 17 | 23 | 25 | 58 |
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.
- Walk the outputs in index order. Collect every OP_RETURN whose data push starts with the four magic bytes.
- If there is more than one, record
VOID_DUPLICATE_MARKERand stop treating the transaction as marked. The default rule still applies to any carrier the transaction spends. - If there is exactly one, check the push is single and minimal, and the script is at or under 83 bytes.
- Check the version byte, then the opcode byte.
- Decode the payload for that opcode. A payload of the wrong length is bad grammar for that opcode.
- 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.