I would like to devote this question to listing all information about the 'extra' field, and its standard interpretation and implementation in core software v0.15.x.x. In other words, how is it stored in transaction data, and what are the rules and assumptions/conventions surrounding common tags (i.e. how the field gets parsed)? What follows is my current understanding.
- The field's location within transaction data is defined by the transaction's structure. Currently (protocol v12) the transaction version is v2, and 'extra' lives between 'vout' (outputs) and 'rct_signatures' (ringct data).
- The field is interpreted as an array of bytes with little endian order (indexed 0, 1, 2).
- It contains information sections, which each begin with a 'tag' byte. A tag defines how the bytes after it are interpreted. Wherever relevant, the leftmost byte (lower index) is considered most significant.
- The field's core implementation can be found here: src/cryptonote_basic/tx_extra.h
- Wallet designers should understand the rules for all known tags, in order to mitigate interference with the expectations and behavior of other wallets (especially the core wallet).
- Bytes to be interpreted as 'length' or 'amount' are considered varints.
- The field may have unlimited elements, up to the functional limit of its transaction's maximum weight. Note: in the original CryptoNote specification the extra field was prefixed with a varint (a normal varint occupies up to 8 bytes, with up to 57 bits of information) equal to the field's number of elements. Monero does not do that.
- The standard implementation interprets non-standard extra fields to the best of its ability. The parser works like this. It looks at the first byte. If it is a known tag then if the following bytes obey the tag's rules save the tag's bytes for use elsewhere and skip ahead to the next available byte and check if it's a known tag. If an examined byte is not a known tag, look at the next byte.
Basic tags (in hexidecimal format):
0x00
: padding, ignore and move to the next byte; there can be up to 255 of these in an extra field0x01
: public key, saves the next 32 bytes if they can be converted into an elliptic curve point; most transactions will have one of these, and it corresponds to the transaction public key; when going through an extra field, the wallet will save every valid public key it finds in order to use them to test for output ownership0x02
: extra nonce, the next byte is its length (may be up to 255); this is like an extra field within the extra field, and the utility of that is unclear to me; the byte after its length is an 'extra nonce tag' interpreted according to special rules (see below)0x03
: merge mining tag; this doesn't seem to be used by anyone any more0x04
: additional public keys, the next byte is the number of additional keys (must be convertible to elliptic curve points, 32 bytes each), and the byte after that is the most significant byte of the first additional key; usually only one of these sets per transaction, and there should typically be either 1 or #outputs total amount of public keys per transaction (standard single transaction public key, or at-least-one-ouput-is-a-subaddress case with #outputs transaction public keys)0xDE
: mysterious minergate tag
Special extra nonce tags (tags within the extra nonce section):
0x00
: payment ID, the next 32 bytes are a payment ID in clear text; deprecated as of core implementation v0.15, which means it will be ignored/skipped over by the main wallet0x01
: encrypted payment ID, the next 8 bytes are an encrypted payment ID (note: the core implementation adds a dummy encrypted payment ID to transactions with two outputs if one isn't specified, to improve transaction indistinguishability); only the first one of these found will be used and reported to the user if they happen to own an output in the same transaction (my understanding of best practice is encrypted payment IDs should only be included in 2-output transactions, and that the 'change'-output will ignore payment IDs)
An example from a transaction in block 2004288 (transaction hash 3033917f822512e7b9dab4eb3477cd9ec61052224723623c1353f4d160e8dd68
):
"extra":[1,240,78,249,111,229,39,232,253,137,61,146,133,192,190,13,58,96,239,78,21,116,133,204,111,251,40,170,155,126,208,23,244,2,9,1,253,234,238,74,23,163,141,203] = [1 (transaction public key TAG), ...32 bytes..., 2 (extra nonce TAG), 9 (length of extra nonce), 1 (extra nonce encrypted payment ID TAG), ...8 bytes... ]