Normative · Bitcoin mainnet · btc_stamps 1.9.3

SRC-101 specification

Every rule below states what the reference indexer does, and names the file that does it. Where the code contains a branch that cannot be reached, or a field that is declared and never used, this document says so rather than describing the intention.

How to read the rule numbers

Rules are numbered once and never renumbered. If a rule is withdrawn its number is retired and the changelog records why. A rule marked Observation is not normative: it describes behaviour of the reference implementation that an independent implementer must reproduce to stay in consensus, but which reads as a defect rather than a design.


1. Scope and terminology

Rule 1

SRC-101 is defined on Bitcoin mainnet only. No other chain and no test network is in scope. Nothing in this document describes behaviour on signet, testnet or any fork.

Rule 2

An operation is a UTF-8 JSON object carried in a Bitcoin transaction through the Bitcoin Stamps data carrier. The five operations are deploy, mint, transfer, setrecord and renew. There is no burn, no revoke, no reclaim, no subdomain, and no batch operation other than the multi-name form of mint.

Rule 3

A namespace is created by one deploy and is identified for all time by the transaction hash of that deploy. Every other operation names its namespace through the hash field. Two namespaces may contain the same name without conflict; a name is only unique within its namespace.

Rule 4

A name is carried in the tokenid field as a Base64 string. Its decoded, lower-cased UTF-8 form is the identity used for every comparison and every database lookup. Two Base64 encodings that decode to the same text are the same name.

Rule 5

A registration is a lease with an absolute end. Each entry carries an expire_timestamp in Unix seconds. The block timestamp of the transaction being processed is compared against it. There is no grace period and no redemption window.

Rule 6

The creator of an operation is the address that funded input 0 of the transaction. The destination is the address of output 0, and the destination value is the satoshi value of output 0. Ownership checks compare against the creator; payment checks compare against the destination value.

2. The data carrier

This section is identical in substance to the SRC-20 carrier. If you have implemented one, you have implemented the other. It is repeated here so that this document stands alone.

Rule 7

The carrier prefix is the six ASCII bytes stamp:. The framed buffer is a two-byte big-endian length, then the prefix, then the JSON. The length counts the prefix and the JSON, and does not count itself.

Rule 8

There are exactly two carriers: bare multisig and P2WSH. OP_RETURN is not an SRC-101 carrier. A transaction containing an OP_RETURN is noted as such and the output is otherwise ignored.

Rule 9

Bare multisig. Every output whose script ends in OP_CHECKMULTISIG contributes its public keys. The data chunk is the concatenation of each key with its first and last byte removed. That chunk is decrypted with ARC4, keyed on the byte-reversed transaction hash of the outpoint spent by input 0. After decryption, bytes 2 through 7 must equal stamp:, and the first two bytes give the length. The payload is the bytes after the prefix up to that length.

Rule 10

P2WSH. Every output at index greater than zero whose script is a 32-byte witness program contributes its 32 bytes. The chunks are concatenated in output order, trailing zero bytes are stripped, the first two bytes give the length, and the data that follows must begin with stamp:. A P2WSH-carried payload has its keyburn forced to 1 by the decoder.

Rule 11

P2WSH chunk collection is gated at block 865000, a constant named for SRC-20 but shared by every Stamps sub-protocol. Below that height there is no P2WSH carriage for anything, SRC-101 included. Since SRC-101 does not begin until block 870652, in practice both carriers are available for the whole life of the protocol.

Rule 12

P2WSH takes precedence over multisig, with no fallback. When a transaction carries qualifying P2WSH data, the multisig branch is not attempted, even when the P2WSH data fails its own length check. The source marks this exclusion as consensus-load-bearing. Adding a fallback forks the ledger.

Rule 13

The destination value is read differently by carrier. On the multisig branch the destination value is always the satoshi value of output 0. On the P2WSH branch it is zero until block 940000, at which height the decoder begins reading output 0. Because mint and renew require a payment measured against that value, a P2WSH-carried mint or renew cannot satisfy its payment check below block 940000 unless the required amount is zero. deploy, transfer and setrecord do not read the destination value and are unaffected.

Rule 14

The payload must decode as UTF-8 and parse as a JSON object. All top-level keys are lower-cased before any further processing, so "P", "OP" and "Hash" are normalised to p, op and hash. JSON floating-point literals are parsed as decimals, not binary floats.

3. Classification and activation heights

Rule 15

A payload is classified as SRC-101 when its lower-cased p value, upper-cased, equals SRC-101. The three sub-protocols the carrier recognises are SRC-721, SRC-20 and SRC-101. Any other value makes the payload not a sub-protocol at all.

Rule 16

A transaction is SRC-101 only when the classification in rule 15 holds and keyburn == 1. This is the same keyburn requirement SRC-20 carries. On the P2WSH branch the decoder sets keyburn to 1 itself, so the requirement is effectively a multisig-branch condition.

Rule 17

SRC-101 is valid only at block 870652 and above. Below that height no SRC-101 payload has any meaning. The constant is BTC_SRC101_GENESIS_BLOCK.

Rule 18

At and above block 872200 the mint key set changes: img leaves the required set and key matching for mint alone relaxes from exact to superset. The constant is BTC_SRC101_IMG_OPTIONAL_BLOCK. Rules 21, 48 and 53 give the detail.

Rule 19

At and above block 940000 the decoder reads output 0's value on the P2WSH branch, as rule 13 describes. The constant is BTC_SRC101_OLGA_BLOCK, annotated in the source as approximately March 2026. This height changes payment visibility, not carrier availability.

Every height that changes SRC-101 behaviour
HeightConstantWhat changes
865000BTC_SRC20_OLGA_BLOCKP2WSH chunk collection begins, shared across sub-protocols. Predates SRC-101 genesis
870652BTC_SRC101_GENESIS_BLOCKSRC-101 becomes valid. Nothing before this height counts
872200BTC_SRC101_IMG_OPTIONAL_BLOCKimg becomes optional on mint, and mint key matching relaxes to superset
940000BTC_SRC101_OLGA_BLOCKOutput 0's value becomes readable on the P2WSH branch, so P2WSH mint and renew payments can be satisfied
There is no end block

SRC-20 stops recognising Counterparty-carried operations at block 796000. SRC-101 has no equivalent cut-off. A Counterparty-carried SRC-101 is accepted at any height at or above genesis, and the classification gate does not impose the extra conditions SRC-20 imposes on its Counterparty path. Section 12 states this precisely.

4. The payload and its key sets

Rule 20

Each operation has a fixed key set. For deploy, transfer, setrecord and renew, the payload's key set and the operation's key set are compared with a symmetric difference, and that difference must be empty. Every listed key must be present and no other key may be present. A payload that fails this test is not an SRC-101 operation: it is discarded before any handler runs, leaves no row in any table, and produces no status code.

Rule 21

For mint the comparison depends on height. Below block 872200 it is the same symmetric difference as rule 20, against a set that includes img. At and above 872200 it becomes a containment test: every key in the set must be present, and additional keys are permitted. The set at that height no longer includes img, which is why img becomes optional.

Rule 22

The op value is compared case-insensitively against the five operation names. Any other value causes the payload to be discarded exactly as in rule 20. There is no unsupported-operation record for an unrecognised op at this stage.

The five key sets, verbatim from check_src101_inputs
OperationKeysCountMatching
deploy p, op, root, name, tick, lim, owner, rec, pri, desc, mintstart, mintend, wla, imglp, imgf, idua 16Exact, all heights
mint, below 872200 p, op, hash, toaddress, tokenid, dua, prim, sig, img, coef 10Exact
mint, 872200 and above p, op, hash, toaddress, tokenid, dua, prim, sig, coef 9 requiredSuperset, extras allowed
transfer p, op, hash, toaddress, tokenid 5Exact, all heights
setrecord p, op, hash, tokenid, type, data, prim 7Exact, all heights
renew p, op, hash, tokenid, dua 5Exact, all heights
Rule 23

Because matching is exact, every key in a set is mandatory even when it carries no value. A mint below 872200 that has no whitelist signature must still carry a sig key. The conventional way to satisfy this is the empty string, which the validator normalises to null (rule 27).

Rule 24

A deploy must not carry a hash key. The key set forbids it, and the handler additionally requires that no namespace hash has been resolved.

Rule 25

An operation with a key set naming hash is dispatched only after a namespace lookup succeeds. If hash is absent or does not resolve, the operation is recorded with status IH or ND respectively. ND is one of only two statuses that marks the parse as hard-invalid (rule 75).

Rule 26

The p and op values are upper-cased in the stored record. A row therefore reads SRC-101 and MINT regardless of the case used in the payload.

5. Field rules

Field validation runs before any operation handler. It is fail-closed: see rule 35.

Rule 27

An empty string is normalised to null for every field, before any type-specific rule runs. This is what makes "sig": "" a valid way to satisfy the mandatory-key requirement without supplying a signature.

Rule 28

Numeric fields are lim, dua, idua, mintstart, mintend and coef. Each must match ^[0-9]*(\.[0-9]*)?$ and must coerce to an integer that is at least zero. The stored value is a plain integer, not a decimal. There is no upper bound and scientific notation is rejected by the pattern.

Rule 29 Observation

Numeric coercion treats a JSON string and a JSON number differently. The string "1.5" matches the pattern but fails integer conversion, and the operation is discarded. The JSON number 1.5 matches the pattern and converts to 1, silently truncating. Write integers as strings if you want a malformed value to fail loudly.

Rule 30

tokenid is either a Base64 string or a list of Base64 strings. Each string must match ^[A-Za-z0-9+/]+={0,2}$, have a length that is a multiple of four, and be at most 128 characters in encoded form. The decoded text is lower-cased and must not contain a special character (rule 33). Within a list, duplicates after decoding are rejected. The list form is normalised by re-encoding each decoded name with the standard alphabet; the string form is stored as submitted.

Rule 31 Observation

The list form of tokenid is decoded with the URL-safe Base64 alphabet while the string form is decoded with the standard alphabet. The acceptance pattern in rule 30 permits only the standard alphabet, so - and _ never reach either decoder. The distinction is therefore invisible in practice, but an implementer copying only the decoder will produce a different result for a name containing those characters.

Rule 32

prim accepts only the two strings "true" and "false". A JSON boolean is not accepted: the comparison is textual. Because a field failure is fatal (rule 35), a payload carrying "prim": true is discarded in full.

Rule 33

root and name, and every decoded tokenid, are rejected if they contain a special character. The test rejects any Unicode character in category Zs (space separators) or Cf (format characters), any all-whitespace value, and any character in a fixed punctuation set that covers ASCII punctuation and a range of CJK full-width punctuation. Ordinary letters, digits and most symbols pass.

Rule 34

The remaining typed fields:

FieldRule
hashExactly 64 hexadecimal characters. Stored as the namespace identifier
owner, toaddressMust decode as a Bitcoin address, Base58 or Bech32. Bech32 is accepted
recA list whose every member decodes as a Bitcoin address. Stored de-duplicated, and order is not preserved
wlaA 66-character compressed public key beginning 02 or 03 that is a valid point on secp256k1
priAn object whose keys all parse as integers and are unique, and whose values are all integers. Keys are name lengths, values are prices in satoshis
imglp, imgf, sigMust be strings
imgMust be a list whose members are all strings
tickLower-cased, non-ASCII characters escaped. A SHA3-256 digest of the lower-cased value is stored alongside as tick_hash
type, dataStored unchanged, then combined: the value of data is written to a field named for the value of type with _data appended
descCarried through with no validation. Stored as the description
Rule 35

Field validation is fail-closed. If any field fails its rule, the transaction is abandoned before dispatch. No handler runs, no operation-level status is assigned, and the record carries only the field-level error text. This is the sharpest divergence from SRC-20, where a bad field usually produces an invalid record with a meaningful status code and the operation is still reasoned about.

Rule 36 Observation

The source declares two regular expressions named for Bitcoin and Ethereum address validation. Neither is ever referenced. Address validation is delegated to helpers that decode Base58 and Bech32 for Bitcoin, and apply a separate inline pattern for Ethereum. Reading the declared Bitcoin pattern and implementing it would wrongly reject every Bech32 address, since the declared pattern is Base58-only and caps length at 34 characters.

6. deploy

Opens a namespace. It creates no name and claims nothing.

Rule 37

Length limits are enforced on six fields. root, name, tick and imgf must be shorter than 32. imglp must be shorter than 255. pri must have fewer than 255 entries. Failure gives status IDP.

Rule 38

A missing value in any of those six is treated as over-length, because the length test substitutes an over-long placeholder when the field is absent. Combined with the exact key set of rule 20, all six are effectively mandatory and must carry a value.

Rule 39

When mintend is missing or zero it is stored as 18446744073709551615, which is 2^64 minus 1. This is the only place that constant appears in SRC-101, and it functions as "no end".

Rule 40

lim is never enforced as a limit. No handler reads it as a cap on mints, names, or anything else. Its only functional role is as a presence sentinel: the namespace lookup treats a resolved lim of zero as "no such namespace". A namespace deployed with lim zero is permanently unusable, and every later operation against it fails with ND.

Rule 41

mintstart and mintend are compared against the block timestamp, not the block height. They are Unix seconds. The window is half-open: minting requires the block timestamp to be at least mintstart and strictly less than mintend.

Rule 42

The pri object is written to a price table, one row per entry, keyed by name length. The key 0 acts as the fallback price for any length that has no explicit entry. A name whose length has no entry and where no 0 entry exists cannot be priced, and the operation fails with IRL.

Rule 43

rec is written to a recipients table. It is the set of addresses that may collect mint and renew payments; owner is stored but is not used in any payment or authorisation check.

Rule 44 Observation

The in-block namespace lookup matches on a field that a deploy row never carries, because a deploy has no hash key and therefore no resolved namespace identifier. Consequently a namespace cannot be resolved from within the block that deployed it, and the database is only written after the block completes. A mint in the same block as its deploy fails with ND. Wait for the deploy to confirm before minting.

7. mint

Claims one or more free names in a namespace for a paid term. The checks below run in the order given; the first failure ends the operation.

Rule 45

The transaction's destination must be a member of the namespace's rec list. A payment to any other address, however large, gives status IR.

Rule 46

tokenid must be a non-empty list for a mint. The single-string form is rejected with ITT. A mint of one name is a list of one.

Rule 47

coef must be an integer in the range 0 to 1000 inclusive. It expresses a discount in parts per thousand. Out of range gives ITC.

Rule 48

Below block 872200, img must be a list. At and above 872200, an absent img is replaced by a list of nulls the same length as tokenid, and a present-but-not-a-list img gives ITI.

Rule 49

dua must be an integer greater than zero, and the namespace's idua must be greater than zero. Failures give ITD and ITID. The submitted dua is then rounded up to the next multiple of idua, and the number of rounds is dua / idua. The rounded value, not the submitted value, is stored and used everywhere afterwards.

Rule 50

The required payment is the sum, over each name being minted, of the price for that name's decoded length multiplied by the number of rounds. Prices come from the namespace's price table, with the 0 entry as fallback.

Rule 51

The whitelist discount. The effective coefficient starts at 1000, meaning full price, and coef is ignored unless proved. If sig is present and non-empty, the indexer verifies an ECDSA signature over SHA-256, against the namespace's wla public key, over the JSON serialisation of one of two payloads, tried in order:

{"hash": <deploy hash>, "coef": "<coef>", "address": <creator>,
 "tokenid": <tokenid as submitted>, "dua": "<rounded dua>"}

{"hash": <deploy hash>, "coef": "<coef>", "address": <creator>,
 "dua": "<rounded dua>"}

If either verifies, the effective coefficient becomes coef. If neither does, the operation fails with IRS. Note that coef and dua are serialised as strings, that dua is the value after rounding, and that tokenid is the value as submitted rather than the normalised form.

Rule 52

The payment check is destination value >= required payment × effective coefficient / 1000. Failure gives IRV. Overpayment is accepted and is not refunded by the protocol.

Rule 53

When the namespace declares both imglp and imgf, an image URL is derived per name as imglp then the decoded name then . then imgf. Below block 872200 the submitted img entries must equal the derived values exactly, or the operation fails with IRM. At and above 872200 the submitted values are discarded and the derived values are written in their place.

Rule 54

The block timestamp must be at least mintstart (else UT) and strictly less than mintend (else OT).

Rule 55

Names already held with a live expiry are removed from the list and the mint proceeds without them. For each name, the current entry is looked up; if its expiry is later than the block timestamp the name is dropped, and otherwise the current holder is recorded as the new entry's preowner. Only if the list becomes empty does the operation fail, with status DM. There is no refund and no notification for the dropped names.

Rule 56

The new expiry is block timestamp + rounded dua × 31536000. The constant 31536000 is 365 days in seconds, so dua is a count of 365-day years. Leap seconds and leap days are not modelled.

Rule 57

The new owner is the value of toaddress, which need not be the creator. The entry's resolved Bitcoin address is set to that same owner, its resolved Ethereum address is left empty, and its text record is left empty.

8. transfer

Rule 58

The name must already have an entry with an owner and an expiry, else NM. The creator must equal the current owner, else NO. The block timestamp must be strictly less than the expiry, else OE.

Rule 59

tokenid for a transfer is the single-string form. The lookup compares a scalar decoded name; supplying a list produces a lookup that cannot match.

Rule 60

The expiry is carried over unchanged. A transfer does not extend, reset or shorten the term. Buying a name late in its term buys the remainder of that term.

Rule 61

Records do not survive a transfer. The owners row written for a transfer sets the resolved Bitcoin address, the resolved Ethereum address and the text record to empty, and the primary flag to false. The new owner must issue a fresh setrecord to point the name anywhere.

Rule 62

There is no payment check on a transfer. The destination value is not read, and no fee is owed to the namespace.

9. renew

Rule 63

The name must have an entry, the creator must be the current owner, and the block timestamp must be strictly less than the current expiry. Renewing an expired name is refused with OE. This is the rule with the largest practical consequence in the protocol.

Rule 64

dua is validated and rounded exactly as in rule 49. The required payment is the price for the length of the decoded name, with the 0 entry as fallback, multiplied by the number of rounds.

Rule 65

A renew gets no whitelist discount. There is no coef and no sig in the renew key set, and the payment check is a plain destination value >= required payment. A holder who minted at a discount renews at full price.

Rule 66

The new expiry is the existing expiry plus the term, not the block timestamp plus the term. Renewing early loses nothing. The handler contains a branch that would restart the clock from the block timestamp for an already-expired entry, but rule 63 makes that branch unreachable.

Rule 67

Owner, records and primary flag are all carried over unchanged by a renew.

10. setrecord

Rule 68

The name must have an entry, the creator must be the current owner where a creator is present, and the block timestamp must be strictly less than the expiry. The three failures are NM, NO and OE.

Rule 69

The pair type and data is combined into a field named <type>_data. The two the handler understands are address and txt, giving address_data and txt_data. At least one of the two must be present and non-empty, else ID. A type the handler does not understand leaves both empty and therefore also gives ID.

Rule 70

address_data is an object. Its btc member, when present and non-empty, must decode as a Bitcoin address. Its eth member, when present and non-empty, is not an address but a signature: the hexadecimal signature of an Ethereum personal-sign message whose text is the hexadecimal, byte-reversed hash of the transaction spent by input 0. The signer is recovered from that signature, validated as an Ethereum address, and stored with the leading 0x removed.

Rule 71

When prim is true, address_data must be present and its btc member must equal the creator, else IDB. You cannot mark a name primary for an address you are not currently spending from.

Rule 72

Setting the primary flag clears it from every other name that the same resolved Bitcoin address holds in the same namespace. One primary name per address per namespace.

Rule 73

A setrecord that supplies only one of the two record kinds leaves the other as it was. Records are merged, not replaced wholesale. Only a transfer clears them (rule 61).

11. Validity and state

Rule 74

There are three outcomes, and they are not the same thing:

Valid
The handler completed and marked the record valid. A row is written to both the operation table and the valid table, and the owners projection is updated.
Invalid
The handler ran and assigned a status code. A row is written to the operation table with that status. Nothing changes in the register.
Excluded
The payload never became an SRC-101 operation. Failed key matching, an unrecognised op, a failed field rule, or a failed classification. No row anywhere, no status code.
Rule 75

Only ND and UO mark the parse itself as invalid; every other status is advisory. The block processor discards that flag and keys everything on whether the record was marked valid, so in practice the distinction has no effect on stored state.

Rule 76

Five tables hold SRC-101 state: SRC101 for every parsed operation, SRC101Valid for the valid subset, src101price for deploy prices, recipients for deploy recipient lists, and owners for the current register. The reference describes each.

Rule 77

SRC-101 contributes nothing to the block ledger hash. The hash is computed from valid stamps and the SRC-20 string only. There is no cross-implementation consensus check for SRC-101 comparable to the SRC-20 ledger-hash comparison, so an SRC-101 divergence between two indexers can persist silently.

Rule 78

SRC-101 shares no state with the SRC-20 ledger. No SRC-101 operation reads or writes an SRC-20 balance, and no SRC-20 operation touches a name. A transaction is classified as one protocol or the other, never both.

Rule 79

Within a block, operations are processed in transaction order, and a name minted earlier in a block is visible to a later transaction in the same block. Namespaces are the exception, per rule 44.

Rule 80

There is no mempool state. An unconfirmed transaction has no SRC-101 meaning. Nothing about a name changes until the block containing the operation is processed.

12. The Counterparty relationship

Rule 81

An SRC-101 transaction that also carries a Counterparty asset identifier is accepted, at any height at or above genesis. The classification is a plain disjunction: SRC-101 with an asset identifier, or SRC-101 without one. Both branches are open forever.

Rule 82

The Counterparty branch for SRC-101 imposes none of the extra conditions that the equivalent SRC-20 branch imposes. SRC-20 requires the asset identifier to begin with A, requires a supply of zero, and stops at block 796000. SRC-101 requires only that an asset identifier is present.

Rule 83

An indexer that wants to serve SRC-101 alone still needs Counterparty issuance data available, because the classification gate reads whether an asset identifier was resolved for the transaction. It does not need Counterparty history for SRC-101 state, since no SRC-101 state predates block 870652.


Sources for this document

  • indexer/src/index_core/src101.py for rules 20 to 73 and the status codes.
  • indexer/src/config.py for rules 11, 17, 18, 19, 39 and the table names in rule 76.
  • indexer/src/index_core/models.py for rules 14, 15, 16, 17, 81 and 82.
  • indexer/src/index_core/transaction_utils.py for rules 7 to 13.
  • indexer/src/index_core/database.py for rules 42, 43, 44, 76 and the projection described in the reference.
  • indexer/src/index_core/blocks.py for rules 75, 77 and 79.
  • indexer/src/index_core/util.py for rules 30, 33, 34 and 36.