Conformance

Test vectors

Cases a conforming reader must reproduce exactly. Every expected outcome is the behavior of the bitcoin-universe-block20-v1 profile, and every rejection cites the rule that causes it.

Setup

Shared fixture

Unless a vector says otherwise, all vectors share this state.

Network
Bitcoin mainnet
Ticker
BLK, identity blk
Deploy height (D)
767430
Maximum supply
21000000
Per-mint limit
1000
Anchor block used
000000000000000000029730547464f056f8b6e2e0a02eaf69c24389983a04f5 at height 767430
Freshness window (W)
144 blocks
Finality depth
6 confirmations

The anchor block above is a real Bitcoin mainnet block: it is the first height a BLOCK-20 reader scans on mainnet. Ticker, supply, and reveal heights are fixture values.

Anywhere a vector writes <HASH>, substitute that 64 character block ID. Run any mint vector through the anchor verifier to see the derivation evaluated step by step.

Group G

Payload grammar and field validation

Grammar vectors. Outcome "ignored" means the inscription is not a BLOCK-20 error, it simply has no effect.
IDInscription contentExpected outcomeRule
G1{"p":"block-20","op":"deploy","tick":"BLK","max":"21000000","lim":"1000"}Accepted ticker blk created, all supply figures zeroR5.1 to R5.6
G2{"p":"block-20","op":"mint","tick":"BLK","amt":"1","amt":"2","hash":"<HASH>"}Rejected repeated fieldR3.4
G3{"p":"block-20","op":"mint","tick":"BLK","amt":1000,"hash":"<HASH>"}Rejected value is not a JSON stringR3.3
G4{"p":"block-20","op":"transfer","tick":"BLK","amt":"250"} extraRejected trailing contentR3.2
G5{"p":"block-20","op":"transfer","tick":"BLK","amt":{"v":"250"}}Rejected nested objectR3.3
G6{"p":"block-20","op":"mint","tick":"BLK","amt":"1000","hash":"<HASH>","memo":"hi"}Rejected unknown field on a mintR3.8
G7{"p":"block-20","op":"deploy","tick":"BLK","max":"21000000"}Rejected missing required limR3.8
G8{"p":"block20","op":"mint","tick":"BLK","amt":"1","hash":"<HASH>"}Ignored not a BLOCK-20 messageR3.7
G9{}Ignored parses, but has no pR3.7
G10{"p":"block-20","op":"deploy","tick":"blk","max":"21000000","lim":"1000"} after G1Ignored identity blk already deployedR4.3
G11{"p":"block-20","op":"deploy","tick":"BLOCK","max":"100","lim":"1"}Accepted five characters is the maximum, identity blockR4.1
G12{"p":"block-20","op":"deploy","tick":"BLOCK2","max":"100","lim":"1"}Rejected six charactersR4.1
G13{"p":"block-20","op":"transfer","tick":"BLK","amt":"0"}Rejected zero is not a positive integerR4.4
G14{"p":"block-20","op":"transfer","tick":"BLK","amt":"0250"}Rejected leading zeroR4.4
G15{"p":"block-20","op":"transfer","tick":"BLK","amt":"2.5"}Rejected decimal point, and BLOCK-20 has zero decimalsR4.4, R4.6
G16{"p":"block-20","op":"deploy","tick":"OVR","max":"1000","lim":"1001"}Rejected per-mint limit exceeds maximum supplyR5.3
G17Deploy with a des of 257 bytesRejected description over 256 bytesR5.4
G18Any valid payload inscribed with content type image/pngIgnored content type not acceptedR2.2
G19Any valid payload padded to 2049 bytesIgnored over the size ceiling, not even parsedR2.3

Group A

Anchor boundaries

All vectors use the mint payload {"p":"block-20","op":"mint","tick":"BLK","amt":"1000","hash":"<HASH>"} with D = 767430 and W = 144. Only the anchor height A and the reveal height R vary. These are the cases where implementations diverge, so test every one.

Anchor derivation vectors, including all four boundaries.
IDARR - AExpected outcomeRule
A176743076750070Valid credits 1000. Boundary case A = D.R6.6
A276742976750071Rejected anchor is older than the deploymentR6.6
A37674997675001Valid credits 1000. Boundary case A = R - 1.R6.7
A47675007675000Rejected a mint cannot anchor to its own blockR6.7
A5767501767500-1Rejected anchor is after the revealR6.7
A6767356767500144Valid credits 1000. Boundary case R - A = W.R6.8
A7767355767500145Rejected one block outside the freshness windowR6.8
A8hash is uppercase hexRejected before any chain lookupR6.4
A9hash has a 0x prefix, or is 63 or 65 charactersRejected syntaxR6.4
A10hash is a valid transaction ID, not a block IDRejected resolves to no blockR6.5
A11hash names a header the node knows, but that height now holds a different blockRejected the anchor is on an orphaned branchR6.5
A12hash names a block with zero confirmationsRejected not on the active chainR6.5

A1, A3, A6, and A7 are the vectors that catch off-by-one errors. An implementation that uses A <= R passes A4 incorrectly. One that uses R - A < W fails A6 incorrectly. One that uses A > D fails A1 incorrectly.

Group S

Limit and supply

Per-mint limit and supply exhaustion vectors. Anchor is valid in every case.
IDamtMinted beforeExpected outcomeRule
S110000Valid credits 1000. Equal to lim is allowed.R6.3
S210010Rejected above the per-mint limit. Not reduced to 1000.R6.3
S3100020999500Valid credits 500. Partial final mint: requested 1000, credited 500.R6.10
S4100021000000Ignored supply exhausted. No event is produced at all.R6.9
S5121000000Ignored same as S4, regardless of amountR6.9

After S3 the ticker's minted supply is exactly 21000000, total is 21000000, and every later mint follows S4.

Group T

Transfer and settlement

Starting state for this group: address ALICE holds 1000 BLK with no reservations.

Transfer inscription and settlement vectors.
IDActionExpected outcomeRule
T1ALICE reveals {"p":"block-20","op":"transfer","tick":"BLK","amt":"250"}Accepted reservation created. Balance stays 1000, available becomes 750. No supply change.R7.5
T2After T1, ALICE reveals a second transfer for 800Ignored available balance is 750, below 800. No reservation created.R7.3
T3After T1, ALICE reveals a second transfer for 750Accepted available becomes 0. Two independent reservations now exist.R7.3
T4After T1, the inscription moves to BOBSettled ALICE 750, BOB 250. Supply unchanged.R7.7
T5After T1, the inscription moves back to ALICESettled ALICE 1000, reservation released, movement still recorded.R7.7
T6After T1, the inscription is spent in transaction feesSettled ALICE 1000, reservation released. No supply change.R7.8
T7After T1, the inscription is burnt or sent to a destination with no supported addressSettled ALICE 750. Total and circulating fall by 250, burned rises by 250, minted unchanged.R7.9
T8After T4, BOB moves the same inscription to CAROLNo effect the reservation is already settled.R7.10
T9A transfer inscription for a ticker that was never deployedIgnored no active deployment.R7.2
T10After T7, ALICE mints again while minted supply is still below maxValid burning did not free capacity, but it also did not consume any.R7.9, R8.6

Group O

Within-block ordering

Fixture change for this group: ORD is deployed with max = 1500 and lim = 1000, and nothing has been minted.

Ordering vectors. Both mints have valid anchors and land in the same block.
IDSituationExpected outcomeRule
O1Two mints of 1000 ORD in one block, at transaction index 3 and 7Index 3 credits 1000. Index 7 credits 500 as a partial final mint. Minted supply reaches 1500.R8.2, R8.3, R6.10
O2Same block, both mints at transaction index 3, inscription numbers 100 and 101Inscription 100 credits 1000, inscription 101 credits 500. Order is by ordinal, then inscription ID.R8.2
O3A transfer settlement and a new inscription share transaction index 5The settlement is processed first, so the released or moved balance is visible to the new inscription in the same block.R8.2
O4A deploy of NEW and a mint of NEW in the same block, deploy ordered firstThe deploy is accepted. The mint is accepted only if its anchor is an earlier block, since A must be below R and D equals R here.R8.4, R6.7
O5The same pair with the mint ordered firstThe mint is ignored: no active deployment existed when it was processed.R6.2, R8.3

Group X

Reorganization

Reorganization vectors.
IDSituationExpected outcomeRule
X1A block containing a mint, a transfer inscription, and a settlement is replacedAll three events are invalidated in descending order of height, transaction index, then event index. Their recorded deltas are reversed exactly. The settled reservation is restored and the created reservation is removed.R10.2, R10.3
X2The replacement branch contains the same mint inscription at a new height, and the anchor block is still on the active chainThe mint is re-evaluated against the new reveal height. If it still satisfies R6.6 through R6.8 it is credited again, otherwise it is not.R10.7
X3The reorganization orphans the anchor block itselfThe mint is invalid on replay even though its bytes are unchanged, because the hash no longer resolves on the active chain.R6.5, R10.7
X4A deploy is invalidated while mints of that ticker are still appliedFatal. The reader must not leave applied events attached to an inactive deployment, and must stop instead.R10.4
X5A reorganization would require invalidating an event at or below the finalized heightFatal. The reader refuses and stops rather than produce a state it cannot justify.R10.5
X6A checkpoint arrives with a lower height, or the same height with a different hash, and no invalidationsRejected as incomplete reorganization data if any applied event lies outside the new chain.R10.1

X4, X5, and X6 are the vectors that separate a careful reader from a lossy one. Each is a case where continuing would produce balances the reader could not derive from the chain, and the correct behavior is to stop.