Categories: Uncategorized

How Bitcoin Script Works: Complete Programming Guide

Most developers first encounter Bitcoin Script while building something on the Bitcoin network and immediately hit a wall. Unlike familiar programming languages, Bitcoin doesn’t run your code — it validates conditions you’ve pre-encoded into transactions. The language isn’t designed for general computation; it’s a purpose-built validation engine that determines whether a transaction output can be spent. Once you internalize this distinction, everything else clicks into place.

This guide treats Bitcoin Script as what it actually is: a stack-based, intentionally limited programming language embedded in Bitcoin’s protocol. I’ll walk through its architecture, walk through real opcodes with working examples, and show you exactly how transaction validation works under the hood. By the end, you’ll be able to read a P2PKH script and understand why certain design decisions were made — and why they matter for Bitcoin’s security model.

What Bitcoin Script Actually Is

Bitcoin Script lives inside every transaction output on the Bitcoin network. When you send bitcoin to someone, you’re not just transferring a number — you’re locking those satoshis with a small program that specifies who can unlock them later. That program is Bitcoin Script, written in a tiny domain-specific language that runs inside Bitcoin nodes during transaction validation.

The language is stack-based, meaning operations work by pushing data onto a stack and popping it off to perform computations. Think of it like a calculator with reverse Polish notation: you push values, then apply operations that consume them and push results back. This simplicity is intentional. Bitcoin Script is not Turing-complete — it lacks loops and most forms of recursion — which means every script can be analyzed for computational complexity before execution. This is a security feature, not a limitation.

Here’s a minimal example of what a locking script (called a scriptPubKey) actually looks like:

OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

This is the P2PKH format, and it appears in virtually every Bitcoin transaction you’ve ever made. We’ll break down what each opcode does in the next section.

The Stack Machine Architecture

Bitcoin runs on a virtual machine called the Script Interpreter. It maintains no persistent state between transactions — just a stack, an alt stack, and a few counters for operations that require them. When a node validates a transaction, it combines the locking script from the output being spent with an unlocking script (called scriptSig) provided by the spending input, then executes the combined program.

The key insight is this: the unlocking script’s job is simply to put the right data on the stack so the locking script’s conditions are satisfied. The locking script never runs on the sender’s machine. It runs on every node that receives the transaction, during validation.

Consider a simple Pay-to-Public-Key-Hash (P2PKH) transaction. Alice wants to send bitcoin to Bob. Bob gives Alice his public key hash (the address). Alice creates a transaction output with a locking script that demands a signature from the corresponding private key. When Bob spends that output, his unlocking script provides two things: the signature and his full public key. The locking script then verifies the public key hashes to the expected hash, then verifies the signature is valid for the transaction.

This two-step verification — hash check plus signature check — is the foundation of Bitcoin’s security model.

Opcodes That Actually Matter

Bitcoin Script contains approximately 90 opcodes, but you can understand most transaction validation with fewer than a dozen. Here are the important ones:

Stack Operations:
OP_DUP — duplicates the top stack item
OP_HASH160 — performs SHA-256 followed by RIPEMD-160 on the top item
OP_EQUAL — pushes 1 if the top two items are equal, else 0
OP_EQUALVERIFY — like OP_EQUAL but fails the entire script if false

Cryptographic Operations:
OP_CHECKSIG — verifies a signature against a public key
OP_CHECKMULTISIG — verifies signatures against multiple keys (M-of-N)

Control Flow:
OP_IF / OP_ELSE / OP_ENDIF — conditional execution
OP_RETURN — terminates execution and marks the output as provably unspendable

The remaining opcodes handle numeric operations, bitwise logic, and data manipulation. The important thing isn’t memorizing all of them — it’s understanding that they all operate on the stack in predictable ways.

Let’s trace through the P2PKH script line by line to see this in action:

OP_DUP                 // [signature] [pubKey] → [signature] [pubKey] [pubKey]
OP_HASH160 <hash>     // [signature] [pubKey] [pubKeyHash]
OP_EQUALVERIFY        // verifies pubKey hashes to hash, removes both if true
OP_CHECKSIG           // verifies signature against pubKey

The result is either a clean stack (success) or script failure (transaction rejected). No loops, no functions, no state — just a deterministic check.

P2PKH and P2SH: Two Patterns You Need to Know

P2PKH (Pay-to-Public-Key-Hash) is the format used by standard Bitcoin addresses. It locks funds to a hash of a public key, requiring the signer to prove they hold the corresponding private key. The locking script demands both a signature and a public key; then it verifies the key’s hash matches the stored address, and verifies the signature.

P2SH (Pay-to-Script-Hash) is more flexible. Instead of locking to a public key hash, you lock to a hash of an arbitrary script called the redeem script. The person sending funds doesn’t need to know what conditions will be required to spend them — they just need to know the hash. The actual unlocking conditions are provided by whoever has the redeem script.

This distinction matters for smart contract functionality. P2SH lets you encode multisignature requirements, timelocks, hash preimages, or any other condition into a 20-byte hash. When someone spends from a P2SH address, they provide the original script (which hashes to the address) along with the required data to satisfy it.

Here’s what a P2SH redeem script for a 2-of-3 multisig looks like:

OP_2 <pubKey1> <pubKey2> <pubKey3> OP_3 OP_CHECKMULTISIG

The corresponding P2SH address is the hash of this script. To spend from it, you need to provide two signatures that correspond to keys in that list.

How Transaction Validation Actually Executes

When a node receives a transaction, it validates each input by combining the input’s scriptSig with the previous output’s scriptPubKey, then running the result through the interpreter. Here’s the step-by-step process:

First, the node retrieves the UTXO (unspent transaction output) being spent. It reads the scriptPubKey from that output. It also reads the scriptSig from the input that’s attempting to spend it.

Second, the node concatenates them in order: scriptSig first, then scriptPubKey. Both are parsed as individual stack items.

Third, the interpreter executes the combined script from left to right. Every opcode pops its required arguments from the stack, performs its operation, and pushes the result back.

Fourth, if the script executes successfully with a non-zero value on the stack, the input is valid. If execution fails at any point or the stack is empty or zero, the input is invalid and the entire transaction is rejected.

This matters because it means the unlocking conditions are always provided by the spender, but the locking conditions are set by the previous owner. The network enforces the lock; the user provides the key.

The absence of loops in Bitcoin Script is directly tied to this validation model. Because every script is guaranteed to terminate, nodes can predict exactly how much computational work any transaction will require. This prevents denial-of-service attacks where malicious scripts could consume infinite CPU. Bitcoin Script is, by design, a validation language — not a computation language.

Limitations That Are Actually Features

If you’re coming from a general-purpose programming language, Bitcoin Script will feel severely restricted. No loops. No function calls. No external data sources. No floating-point arithmetic. You might wonder: why build an entire cryptocurrency on such a limited foundation?

The answer is verification efficiency and security. Bitcoin’s consensus rules must be identical across thousands of independent nodes. Any ambiguity in how a script executes — any dependence on external state, any possibility of non-termination — creates the risk of consensus failures. By restricting Bitcoin Script to simple stack operations with bounded execution, Satoshi created a system where every node can validate every transaction identically, with predictable resource costs.

The trade-off is that complex smart contracts require different tooling. This is why the Bitcoin ecosystem developed other layers — the Lightning Network handles stateful payment channels, RGB and Stacks handle more expressive contracts, and Taproot enables more sophisticated key aggregation. Bitcoin Script itself remains simple by design.

There’s also a practical constraint worth noting: the maximum script size is 10,000 bytes, and the maximum number of opcodes is 201. These aren’t arbitrary limits — they’re consensus rules that every node enforces to maintain predictability.

Reading Real Scripts on the Blockchain

You can open any block explorer, find a transaction, and read the scripts directly. Here’s a P2PKH transaction:

scriptSig (unlocking):

<30440220...> <04a1b2c3...>

scriptPubKey (locking):

OP_DUP OP_HASH160 89abcdefabbaabbaabbaabbaabbaabbaabbaabba OP_EQUALVERIFY OP_CHECKSIG

The scriptSig provides the signature and full public key. The scriptPubKey hashes the public key and compares it to the embedded hash, then verifies the signature. That’s the complete validation logic for every standard Bitcoin payment.

For P2SH, the scriptSig contains the redeem script and whatever data satisfies it:

scriptSig:

<0> <sig1> <sig2> <redeemScript>

The interpreter first evaluates the redeem script (which is just pushed to the stack), then executes it using the remaining stack items.

You can verify this yourself. Pick any transaction on blockstream.info or mempool.space, scroll to the input details, and read the hex. Every script is just a sequence of opcodes and data push operations.

Where Bitcoin Script Fits in the Wider Ecosystem

Developers working on Bitcoin applications need to understand that Script is a low-level building block, not a full development environment. Writing complex logic directly in Script is painful and often impractical. The standard approach is to design your protocol at a higher layer — defining what conditions must be satisfied — then compile those conditions into a Script that enforces them.

The most common real-world use cases remain straightforward: single-key P2PKH transactions, 2-of-3 multisig custodianship, and simple timelock refunds for payment channels. More elaborate constructions — discrete log contracts, stateless coinswaps, coinjoin variants — all build on these same primitives, combining hashlocks, timelocks, and multisig thresholds in increasingly creative ways.

Since the Taproot upgrade in November 2021, Bitcoin Script also gained access to Schnorr signatures and Merkle branches, enabling key aggregation and more efficient multisig setups. The P2TR (Pay-to-Taproot) format allows you to combine multiple spending conditions into a single public key, where the actual conditions used remain hidden until spending. This is a significant upgrade for privacy and efficiency, but the underlying Script engine hasn’t changed — it’s still stack-based, still non-Turing-complete, still deterministic.

Moving Forward

If you’re building on Bitcoin, the skill that pays off most is reading scripts fluently. Don’t worry about memorizing every opcode immediately. Start by identifying the pattern — P2PKH, P2SH, P2TR — and tracing through how the stack changes from input to output. Once you can read a script and predict what conditions it enforces, you’re equipped to design your own.

The deeper insight is that Bitcoin’s security model depends on this simplicity. Every additional feature in a smart contract language introduces potential attack surface, ambiguity, and consensus risk. Bitcoin Script stays lean because lean code is auditable code. When you’re dealing with a financial system that needs to survive decades without a central coordinator, that constraint isn’t a drawback — it’s the entire point.

Robert Garcia

Award-winning writer with expertise in investigative journalism and content strategy. Over a decade of experience working with leading publications. Dedicated to thorough research, citing credible sources, and maintaining editorial integrity.

Share
Published by
Robert Garcia

Recent Posts

10 Grand in Rupees – Instant Conversion Calculator

Instantly convert 10 grand in rupees with our real-time currency calculator. Get accurate USD to…

1 month ago

Gold Price Predictions: Where Will Prices Be in 5 Years?

Get expert gold price predictions for the next 5 years. Discover where gold prices are…

1 month ago

ETH to AED – Convert Ethereum to Dirham Instantly

Convert eth to aed instantly with live rates. Get accurate UAE Dirham value for your…

1 month ago

Larry Fink Net Worth: Inside the BlackRock CEO’s Riches

Discover Larry Fink's net worth and how the BlackRock CEO built a massive fortune managing…

1 month ago

1 Cent in Indian Rupees: Exact Conversion Guide

Convert 1 cent in Indian Rupees instantly with our exact guide. Learn accurate rates, simple…

1 month ago

Kai Cenat Net Worth 2024: See How He Built His Fortune

Kai Cenat net worth revealed! Discover how the superstar streamer built his fortune through gaming,…

1 month ago