> For the complete documentation index, see [llms.txt](https://hub.bsvblockchain.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hub.bsvblockchain.org/bitcoin-protocol-documentation/specification/script.md).

# Script

**Script** is the stack-based language used in transaction outputs and inputs. An output's **locking script** (`scriptPubKey`) sets the condition for spending it; a spending input's **unlocking script** (`scriptSig`) supplies the data that satisfies that condition. To validate an input, the node evaluates the unlocking script followed by the locking script against a shared stack; the input is valid if evaluation succeeds with a true value on top of the stack. Because the outcome is a single true/false result, a script is a predicate. There are no loops or jumps.

This page specifies the execution model, the number encoding, the consensus limits by era, and the formal grammar. The individual opcodes are on the [Opcodes](/bitcoin-protocol-documentation/specification/opcodes.md) page.

<figure><img src="/files/YV0iSVNPY2sOo5wnU1xt" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
In the late 60's Charles 'Chuck' Moore developed FORTH as a fully interactive stack based programming environment that ran on a microcontroller and provided the user with a simple, command line entry based means to enter commands and build FORTH words (operations) and programs (functions).

FORTH is different to many programming languages in that it runs 'live' while the programmer is working on their software, allowing new 'words' to be defined, tested, redefined and debugged without having to recompile or restart the system.

Bitcoin Script is heavily inspired from FORTH in its makeup. It adds some grammar changes and removes capabilities such as jump instructions and loops and is different in that way from FORTH
{% endhint %}

## Execution model and consensus rules

This section specifies how the Script engine evaluates a script and the consensus limits that govern that evaluation. The per-opcode behaviour is on the [Opcodes](/bitcoin-protocol-documentation/specification/opcodes.md) page; this page covers the language, the number model, the limits by era, and the grammar.

**Ground truth.** The execution model derives from `EvalScript`/`VerifyScript` in [`src/script/interpreter.cpp`](https://github.com/bitcoin-sv/bitcoin-sv/blob/879fc8b42168dd0e608dafd51b39c6dabad37d4d/src/script/interpreter.cpp), the number model from [`src/script/script_num.h`](https://github.com/bitcoin-sv/bitcoin-sv/blob/879fc8b42168dd0e608dafd51b39c6dabad37d4d/src/script/script_num.h)/`.cpp`, and the limits from [`src/consensus/consensus.h`](https://github.com/bitcoin-sv/bitcoin-sv/blob/879fc8b42168dd0e608dafd51b39c6dabad37d4d/src/consensus/consensus.h). The formal grammar and the Genesis/Chronicle rule changes are sourced from the consensus specs. Code citations are pinned to release **v1.2.2** (commit `879fc8b`).

### Evaluation

To validate a transaction input, the node evaluates its **unlocking script** ([`scriptSig`](/bitcoin-protocol-documentation/specification/transactions.md#input-ctxin), from the spending input) followed by the **locking script** ([`scriptPubKey`](/bitcoin-protocol-documentation/specification/transactions.md#output-ctxout), from the output being spent):

1. The unlocking script is evaluated first. The resulting **main stack** is carried forward; the alt-stack and conditional state are reset before the locking script runs.
2. The locking script is then evaluated against that main stack.
3. Opcodes are processed left to right. Data pushes place one element on the stack; operations pop their inputs and push their results ([Opcodes](/bitcoin-protocol-documentation/specification/opcodes.md)).
4. There is a single main stack shared across the two scripts plus a separate **alt-stack** (`OP_TOALTSTACK`/`OP_FROMALTSTACK`). There are no loops or jumps.
5. The script **succeeds** if, at the end, the stack is non-empty and the top element is *true* (see the truth-value definition on the Opcodes page). Any error (bad opcode, failed `OP_VERIFY`, exceeded limit, …) makes the input — and therefore the transaction — invalid.

The **clean-stack** rule additionally requires that exactly one element remain after evaluation. It is enforced pre-Genesis and by policy after, and (from Chronicle) can be opted out of for transaction version > 1 (see [Malleability opt-outs](#chronicle-malleability-opt-outs)).

### Number encoding

Numeric values (`CScriptNum`) are encoded as follows ([`script_num.h`](https://github.com/bitcoin-sv/bitcoin-sv/blob/879fc8b42168dd0e608dafd51b39c6dabad37d4d/src/script/script_num.h), `int_serialization.h`):

* **Little-endian**, least-significant byte first.
* **Sign-magnitude:** the sign is the most-significant **bit** (`0x80`) of the **last** byte. If the magnitude's top byte would already have that bit set, an extra byte (`0x00` for positive, `0x80` for negative) is appended to hold the sign.
* **Zero** is the empty byte array. Negative zero (`0x80`) is not a distinct value and is not minimally encoded.
* **Minimal encoding:** an encoding must not carry a redundant most-significant byte — `(last_byte & 0x7f) == 0` is non-minimal *unless* dropping it would push the sign bit into the next byte (e.g. `+255` is `0xff 0x00`, which is minimal). This is enforced when the minimal-data flag is active.
* **Range/overflow:** a number longer than the era's maximum serialized length is rejected. When a number is read as a machine integer, `getint`/`getint64` **saturate** to the int32/int64 bounds rather than wrapping.

Pre-Genesis the backing type is a 64-bit integer capped at 4 bytes; from Genesis the engine uses an arbitrary-precision (big-integer) backing bounded only by the maximum-script-number-length limit.

### Limits by era

All values from [`src/consensus/consensus.h`](https://github.com/bitcoin-sv/bitcoin-sv/blob/879fc8b42168dd0e608dafd51b39c6dabad37d4d/src/consensus/consensus.h#L37-L82):

| Limit                        | Pre-Genesis                   | Genesis                                                     | Chronicle                   |
| ---------------------------- | ----------------------------- | ----------------------------------------------------------- | --------------------------- |
| Max operations per script    | 500 (was 201 before Nov 2018) | unlimited (`UINT32_MAX`)                                    | —                           |
| Max script size              | 10,000 bytes                  | unlimited (`UINT32_MAX`, P2P-bounded)                       | —                           |
| Max script number length     | 4 bytes                       | 750,000 bytes                                               | 32 MB (`32 × ONE_MEGABYTE`) |
| Max script element size      | 520 bytes                     | removed (see stack-memory rule)                             | —                           |
| Max stack elements           | 1,000                         | removed (see stack-memory rule)                             | —                           |
| Max public keys per multisig | 20                            | `UINT32_MAX` (`2³²−1`)                                      | —                           |
| Stack memory usage           | n/a                           | `Σ(32 + element_size)` over stack + alt-stack, configurable | —                           |

A dash means the value is unchanged from the previous era. From Genesis, the per-element and per-count stack limits are replaced by a single **stack memory usage** consensus rule: the sum over every element on the main and alt stacks of `32 + (element size in bytes)` must not exceed the configured limit. That limit is a consensus value miners coordinate on; the specific number is operator policy.

### Formal grammar (Genesis)

From Genesis, a script must be **grammatically valid** — it is parsed against a formal grammar, and a script that does not conform is invalid. The grammar's structural core is (full production list in the Genesis specification):

```
<script>          ::= <unlocking-script> <locking-script>
<unlocking-script> ::= <constants> | <empty>
<locking-script>  ::= <script-block> ["OP_RETURN" <non-script-data>]
<script-block>    ::= <function> [<script-block>]
                    | <push-data> [<script-block>]
                    | <branch> [<script-block>]
                    | "OP_RETURN" [<script-block>]
                    | <empty>
<branch>          ::= <op-if> <script-block> ["OP_ELSE" <script-block>] "OP_ENDIF"
<op-if>           ::= "OP_IF" | "OP_NOTIF"
<constants>       ::= <constant> [<constants>]
```

Two consequences are worth calling out:

* **PUSHDATA-only unlocking scripts:** from Genesis, `<unlocking-script>` may contain only constants — i.e. an unlocking script may contain **only** data pushes, no functional opcodes. (This is partially reversed at Chronicle for transaction version > 1 — see below.)
* **`OP_RETURN` and trailing data:** a locking script may end with [`OP_RETURN`](/bitcoin-protocol-documentation/specification/opcodes.md#opreturn-semantics) followed by arbitrary non-script data, which is not parsed as script.

### Chronicle malleability opt-outs

At Chronicle, transactions signed with a **version field greater than 1** may opt out of a set of malleability-prevention restrictions during script evaluation (transactions with version 1 keep them). Each restriction below links to where it is defined in these docs:

* the **minimal-encoding** requirement — numbers and data pushes must use their shortest form (see [Number encoding](#number-encoding) and the [minimal-push rule](/bitcoin-protocol-documentation/specification/opcodes.md#minimal-push-rule));
* **LOW\_S** — a signature's `S` value must be in the lower half of the curve order, giving a canonical signature (part of the signature-encoding checks applied by [`OP_CHECKSIG`](/bitcoin-protocol-documentation/specification/opcodes.md#cryptography); see also [Sighash](/bitcoin-protocol-documentation/specification/signature-hashing.md));
* **NULLFAIL / NULLDUMMY** — a failed signature check must leave an empty signature, and the extra `OP_CHECKMULTISIG` dummy item must be empty (see [`OP_CHECKSIG`/`OP_CHECKMULTISIG`](/bitcoin-protocol-documentation/specification/opcodes.md#cryptography));
* **MINIMALIF** — the condition consumed by [`OP_IF`/`OP_NOTIF`](/bitcoin-protocol-documentation/specification/opcodes.md#flow-control) must be exactly `OP_0` or `OP_1`;
* the [**clean-stack**](#evaluation) requirement — exactly one element may remain after evaluation;
* the **data-only-in-unlocking-script** requirement (from the Genesis [grammar](#formal-grammar-genesis)) — functional opcodes are again permitted in unlocking scripts.

When an unlocking script does run functional opcodes, note that after it finishes the main stack is carried into the locking script but the alt-stack and conditional state are cleared, and an `OP_RETURN` in the unlocking script ends only the unlocking script (not the whole evaluation). The maximum script-number length is also raised from 750,000 bytes to 32 MB at Chronicle.

See the Protocol Upgrade History page for activation heights.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://hub.bsvblockchain.org/bitcoin-protocol-documentation/specification/script.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
