The Rule Nobody Talks About Until Something Breaks

You've been staring at this transaction for two hours. Inputs look right, outputs look right, signatures check out under every tool you have. You broadcast it. The node drops it cold, no error string, nothing. You start pulling at the signature code. Wrong direction.

The problem is a single opcode that pushed a one-byte value onto the stack the wrong way. That's minimal push encoding: one of the quieter consistency rules baked into Bitcoin Script's interpreter, and it has genuine teeth.

What the Rule Actually Says

Bitcoin Script has several opcodes for pushing raw data onto the stack: `OP_PUSHDATA1`, `OP_PUSHDATA2`, `OP_PUSHDATA4`, and a range of single-byte opcodes from `0x01` through `0x4b` that push that many bytes directly. There are also special-case opcodes: `OP_0` pushes an empty byte array, and `OP_1` through `OP_16` push the small integers 1 through 16.

The minimal push rule says use the smallest encoding that fits your data. Always.

If you want to push the integer 1 onto the stack, you must use `OP_1` (opcode `0x51`). You cannot use `0x01 0x01`, meaning "push one byte, value 0x01." Both operations land the same byte on the stack, but the second form spends two bytes of script space where one suffices. The interpreter sees that and flags it.

Same logic applies up the size ladder. A 40-byte data chunk must use the direct push opcode `0x28` (decimal 40) followed by the 40 bytes. Using `OP_PUSHDATA1` for it, encoded as `0x4c 0x28` followed by the data, is non-minimal because `OP_PUSHDATA1` is only necessary for payloads of 76 bytes or more. The interpreter counts the bytes, checks whether a shorter opcode would have sufficed, and rejects anything that fails that test.

The full hierarchy: direct push opcodes cover 0 to 75 bytes; `OP_PUSHDATA1` covers 0 to 255; `OP_PUSHDATA2` covers 0 to 65535; `OP_PUSHDATA4` covers everything larger. Each tier is only valid if the data actually requires that tier. That's the whole rule. It is not complicated. It is, however, unforgiving.

A Concrete Walk-Through

Alice is writing a custom script that pushes the number 7 as part of a multisig setup. Her serializer emits `0x01 0x07`: push one byte, value 7. Her friend Bob writes the same script with a different library. His serializer emits `0x57`, which is `OP_7`. Same stack result, one byte instead of two.

Alice's transaction reaches a node running standard policy. The interpreter hits her push instruction and asks: could this data have been pushed with fewer bytes? Yes. `OP_7` would do it. Minimal push violation. The transaction is rejected as non-standard and won't relay.

Bob's transaction sails through.

They both spent an afternoon on this. Only one afternoon produced a confirmed transaction. The numbers aren't hypothetical: Bitcoin Core's `CheckMinimalPush` function, enforced as a standard relay policy, performs exactly this comparison on every data push during script execution.

Why This Exists at All

Two reasons. One practical, one almost aesthetic.

The practical one is transaction malleability. Before stricter encoding rules were widely enforced, an attacker could take a valid transaction, swap a push opcode for a semantically equivalent but differently-encoded one, and produce a transaction with a different txid. The signature still validated (signatures don't cover the scriptSig in legacy inputs), so both forms were valid to miners. This made payment channels and similar constructions genuinely fragile, because you couldn't safely reference a transaction by its id before confirmation. Minimal push encoding closes one avenue for that kind of mutation.

The second reason is simpler: canonical forms are good engineering. If there are fifty ways to push the number 3 onto a stack, you have fifty things to test, fifty edge cases to reason about, fifty opportunities for implementations to diverge quietly. I think collapsing those to one form is obviously correct, and the developers who pushed for it were right to do so.

SegWit made this stronger still. For SegWit inputs, minimal push in the witness stack is consensus-enforced, not merely a relay policy. That distinction is everything. A relay policy means a miner can, in principle, include a non-standard transaction if they choose. A consensus rule means every node on the network rejects a block containing a violation, full stop. Non-minimal witness pushes don't just fail to relay: they would invalidate the block containing them. The protocol treats it as a structural defect, like a bad proof of work.

The Part That Trips Up Developers

The most common mistake is treating this as a signing issue. Developers hunt through their ECDSA code, re-examine DER encoding, tweak sighash flags. The signature is fine. The problem is upstream, in the serializer that assembles the scriptSig before signing ever happens. Debugging in the wrong layer is how you lose a day.

A second trap is zero and negative zero. In Bitcoin's Script number encoding, zero is represented as an empty byte array, and so is negative zero. Pushing a single byte `0x00` to represent zero is non-minimal; `OP_0`, which pushes an empty array, is the correct form. This catches developers who port integer-to-bytes routines from other contexts without accounting for Bitcoin's specific numeric encoding rules. The routine looks correct. It is not.

If you're already emitting `OP_1` through `OP_16` for small integers and using direct push opcodes for anything under 76 bytes, you're in good shape. Most production libraries handle this correctly: Bitcoin Core, btcd, rust-bitcoin, libsecp256k1's surrounding tooling. The danger lives in hand-rolled serializers, academic experiments, or any code that treats Script as generic stack bytecode rather than Bitcoin's specific dialect of it.

One more thing: the rule applies during execution, not just relay validation. A script can be syntactically parseable and still fail at runtime when the interpreter checks the encoding of each push as it processes it. Unit-testing a script's logical behavior is not enough. You have to run it through an interpreter that enforces encoding rules, not just one that simulates stack operations. Think of it like a compiler that accepts your syntax but rejects your semantics: the script parses fine and then detonates on contact with a real node.

The Bitcoin Script interpreter was designed to be a narrow, consistent machine. Minimal push encoding is one of the bolts that holds that consistency together. Build to the spec.