Glossary

Bitcoin Script

A stack-based programming language used to define spending conditions for Bitcoin outputs.

Key Takeaways

  • Bitcoin Script is a stack-based, intentionally limited programming language embedded in every Bitcoin transaction. It defines the conditions under which a UTXO can be spent.
  • Script supports common patterns like pay-to-public-key-hash (P2PKH) and pay-to-script-hash (P2SH), as well as newer witness-based formats introduced by SegWit and Taproot.
  • By design, Script lacks loops and general-purpose computation, which prevents denial-of-service attacks and keeps transaction validation predictable across all nodes.

What Is Bitcoin Script?

Bitcoin Script is the programming language that governs how bitcoin can be spent. Every Bitcoin transaction contains scripts: an output script (called scriptPubKey) that locks funds to a set of conditions, and an input script (called scriptSig) that satisfies those conditions to unlock the funds. When a node validates a transaction, it executes these scripts together on a simple stack machine to determine whether the spend is authorized.

Unlike general-purpose languages such as Solidity on Ethereum, Bitcoin Script is deliberately constrained. It has no loops, no recursion, and a limited set of opcodes. This minimalism is a feature: every script is guaranteed to terminate, validation time is bounded, and the attack surface for consensus bugs is kept small. The tradeoff is reduced expressiveness, which the Bitcoin ecosystem addresses through careful protocol upgrades like Taproot and proposals for new opcodes such as those enabling covenants.

Script operates on the UTXO model, where every unspent output carries a locking script. To spend that output, a transaction must provide an unlocking script that, when combined with the locking script, evaluates to true on the stack. This model is explained in depth in the UTXO model vs. account model comparison.

How It Works

Bitcoin Script uses a stack-based execution model. Data and opcodes are pushed onto a stack and processed left to right. There are no variables or named registers: all operations consume values from the top of the stack and push results back.

Stack-Based Execution

A stack is a last-in, first-out data structure. Script instructions either push data onto the stack or pop values off, perform an operation, and push the result. After the entire script executes, the transaction is valid if the top stack value is nonzero (truthy).

Consider a simplified example: verifying that a spender knows a secret value whose hash matches a committed hash.

# Locking script (scriptPubKey)
OP_HASH160 <expected_hash> OP_EQUAL

# Unlocking script (scriptSig)
<secret_value>

# Execution (unlocking + locking combined):
# 1. <secret_value> is pushed onto the stack
# 2. OP_HASH160 pops the value, hashes it, pushes the result
# 3. <expected_hash> is pushed onto the stack
# 4. OP_EQUAL pops both values, compares them
# 5. If equal, pushes 1 (true) — spend is valid

Key Opcodes

Bitcoin Script includes roughly 100 active opcodes. A few are fundamental to nearly every transaction:

  • OP_CHECKSIG: pops a public key and a signature from the stack, verifies the signature against the transaction data, and pushes 1 (valid) or 0 (invalid). This is the core opcode for authorizing spends.
  • OP_HASH160: pops a value, applies SHA-256 followed by RIPEMD-160, and pushes the resulting 20-byte hash. Used in pay-to-public-key-hash scripts.
  • OP_EQUAL: pops two values and pushes 1 if they are identical, 0 otherwise. Often combined with OP_VERIFY (as OP_EQUALVERIFY) to abort the script on failure.
  • OP_IF / OP_ELSE / OP_ENDIF: conditional branching that allows scripts to take different execution paths depending on stack values. This enables constructs like HTLCs used in Lightning channels.
  • OP_CHECKLOCKTIMEVERIFY (OP_CLTV) and OP_CHECKSEQUENCEVERIFY (OP_CSV): enforce timelocks, requiring that a transaction cannot be mined before a certain block height or time, or before a relative delay has passed.
  • OP_CHECKMULTISIG: verifies that M of N provided signatures are valid against the given public keys, enabling multisig wallets.

Common Script Patterns

Most Bitcoin transactions use one of a handful of standardized script templates:

PatternIntroducedDescription
P2PKH2009Pay to Public Key Hash: the original address format (1... addresses)
P2SH2012 (BIP 16)Pay to Script Hash: hides complex scripts behind a hash, revealed at spend time
P2WPKH2017 (SegWit)Pay to Witness Public Key Hash: SegWit single-sig (bc1q... addresses)
P2WSH2017 (SegWit)Pay to Witness Script Hash: SegWit multisig and complex scripts
P2TR2021 (Taproot)Pay to Taproot: Schnorr-based with optional script paths (bc1p... addresses)

The most common pattern historically is P2PKH. Its locking script requires the spender to provide a public key that hashes to the committed value and a valid signature:

# P2PKH locking script (scriptPubKey)
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

# P2PKH unlocking script (scriptSig)
<signature> <publicKey>

# Execution:
# 1. Push <signature> and <publicKey>
# 2. OP_DUP duplicates <publicKey>
# 3. OP_HASH160 hashes the duplicate
# 4. Push <pubKeyHash>
# 5. OP_EQUALVERIFY checks the hashes match (aborts if not)
# 6. OP_CHECKSIG verifies signature against publicKey

P2SH changed the model by letting senders pay to a hash of an arbitrary script. The actual script (called the redeem script) is only revealed when the output is spent. This made complex conditions like multisig practical for everyday use, because the sender only needs to know a single hash regardless of the script's complexity.

SegWit and Witness Programs

Segregated Witness (SegWit) moved signature data (the "witness") outside the traditional transaction structure. P2WPKH and P2WSH place script satisfaction data in a separate witness field, which fixes transaction malleability and reduces effective transaction weight.

A P2WPKH output script is minimal:

# P2WPKH locking script
OP_0 <20-byte-pubKeyHash>

# The witness (not in scriptSig) contains:
# <signature> <publicKey>

This simplification is why SegWit transactions are cheaper: the witness data is "discounted" in block weight calculations, incentivizing adoption.

Taproot and Tapscript

Taproot (activated November 2021) introduced P2TR outputs and a new script version called Tapscript. Taproot combines Schnorr signatures with Merkelized Alternative Script Trees (MAST), allowing a single output to commit to many possible spending paths while only revealing the one actually used. For a deeper look at the cryptographic foundations, see the Taproot and Schnorr signatures explainer.

Tapscript modifies several rules from legacy Script:

  • OP_CHECKSIG and OP_CHECKSIGADD use Schnorr signatures instead of ECDSA, enabling efficient key aggregation and batch validation
  • OP_CHECKMULTISIG is disabled in Tapscript, replaced by OP_CHECKSIGADD for more efficient threshold schemes
  • OP_SUCCESS opcodes replace previously undefined opcodes, providing a clean upgrade path for future soft forks
  • Script size limits are relaxed for Tapscript leaves, enabling more complex spending conditions

A P2TR output can be spent in two ways: via a key path (a single Schnorr signature on the internal key) or via a script path (revealing a Merkle proof and satisfying a Tapscript leaf). The key path is indistinguishable from a simple single-sig spend on-chain, which improves privacy for complex setups like multisig and HTLC-based contracts.

Use Cases

Standard Payments

The vast majority of Bitcoin Script usage is simple single-signature payments: P2PKH, P2WPKH, or P2TR key-path spends. A user proves ownership of the private key corresponding to the output's committed public key, and the script validates the signature.

Multisignature Wallets

Script natively supports M-of-N multisig, where M signatures out of N possible keys are required to authorize a spend. This is widely used for custody solutions, corporate treasuries, and joint accounts. Under Taproot, multisig setups can use key aggregation via protocols like MuSig2, making them appear as single-sig on-chain. See Bitcoin multisig wallets explained for an in-depth guide.

Time-Locked Contracts

Using OP_CHECKLOCKTIMEVERIFY and OP_CHECKSEQUENCEVERIFY, scripts can enforce timelocks that prevent spending before a certain block height or elapsed time. This powers:

  • Lightning channels, where timelocks enforce the penalty mechanism for broadcasting old states
  • Hash time-locked contracts, which combine hash preimage checks with timelocks for atomic conditional payments
  • Inheritance schemes, where funds become spendable by a recovery key after a long delay

Layer 2 Protocols

The Lightning Network and other layer 2 solutions rely heavily on Bitcoin Script. Payment channels use scripts that encode complex state machines: commitment transactions with asymmetric revocation, HTLC outputs with hash and timelock branches, and anchor outputs for fee bumping. The payment channels deep dive covers how Script underpins channel construction.

Relationship to Smart Contracts

Bitcoin Script is sometimes described as Bitcoin's smart contract language, but it differs fundamentally from Turing-complete platforms. Script validates: it checks whether conditions are met. It does not compute arbitrary state transitions or persist data across transactions.

This validation-only model means Bitcoin scripts are stateless. Each script execution is independent, operating only on the data provided in the transaction and the stack. There is no global state, no storage, and no ability for one script to call another. While this limits expressiveness, it makes formal verification more tractable and eliminates entire classes of bugs common in Turing-complete systems (reentrancy, unbounded loops, state corruption).

Tools like Miniscript bridge the gap by providing a structured, composable way to write Bitcoin scripts with formal guarantees about correctness, spending analysis, and optimal witness construction. Miniscript compiles down to standard Bitcoin Script but gives developers a higher-level abstraction.

Risks and Considerations

Limited Expressiveness

Script cannot express arbitrary computations. There are no loops, no floating-point arithmetic, and the opcode set is intentionally restricted. Features that other blockchains implement natively (complex token logic, on-chain oracles, stateful contracts) require layered solutions or protocol-level changes on Bitcoin. Proposals for new opcodes like OP_CTV and OP_CAT aim to extend Script's capabilities while preserving its safety guarantees.

Script Complexity and Security

While individual opcodes are simple, combining them into correct scripts is error-prone. Incorrect scripts can create unspendable outputs (permanently lost funds) or outputs that anyone can spend (immediately stolen funds). Even well-tested scripts can interact with consensus edge cases in surprising ways. Miniscript was developed specifically to address this problem by making script construction more systematic and analyzable.

Consensus Upgrade Constraints

Adding new opcodes or changing Script behavior requires a soft fork: a backwards-compatible consensus change that all nodes must adopt. This process is slow and contentious by design, since any bug in Script evaluation can compromise the entire network. The OP_SUCCESS mechanism introduced by Tapscript streamlines future upgrades by reserving opcode slots that can be redefined without invalidating existing scripts.

Privacy Tradeoffs

Complex scripts are visible on-chain when spent, revealing the spending policy to all observers. P2SH and P2WSH hide the script until spend time, but the script is still revealed on the blockchain permanently. Taproot improves this significantly: key-path spends reveal nothing about alternative script paths, and script-path spends only reveal the specific branch used, not the entire tree.

This glossary entry is for informational purposes only and does not constitute financial or investment advice. Always do your own research before using any protocol or technology.