B++ Logo

Bitcoin Script

Bitcoin Script is a programming language that defines how bitcoin can be spent. Every transaction output contains a locking script that sets the spending conditions. To spend that output, you must provide an unlocking script that satisfies those conditions.

TermAlso CalledPurpose
Locking ScriptscriptPubKey, output scriptDefines who can spend and under what conditions
Unlocking ScriptscriptSig, input script, witnessProvides proof that spending conditions are met

How Does It Work?

When you spend bitcoin, the network executes both scripts together:

  1. Unlocking script runs first (pushes signatures, keys, or other data onto the stack)
  2. Locking script runs second (verifies the data satisfies the conditions)
  3. If the stack ends with 1 (true), the spend is valid

Scripts execute on a stack (LIFO). They are intentionally not Turing-complete: no loops means every script terminates, preventing denial-of-service attacks. Fewer features also means fewer vulnerabilities. Build and run scripts in Stack Lab.


When Do Scripts Execute?

Scripts execute when spending, not when receiving. The locking script is stored with the output when bitcoin is sent. It only runs when someone later tries to spend that output.


Script Types

Bitcoin Script has evolved over time, introducing new output types (address formats) that improve security, privacy, and efficiency. Each script type represents a different way to lock and unlock bitcoin.

Script TypeFull NameIntroducedBlockBIPEncodingPrefixKey Feature
P2PKPay‑to‑PubkeyJan 20090---Earliest type. Locks directly to public key. No address format (raw pubkey in script). Rarely used today.
P2PKHPay‑to‑Pubkey‑HashJan 20090-Base581Original address type. Locks to hash of public key. More private than P2PK.
P2MSPay‑to‑MultisigJan 20090---Bare multisig (m-of-n). No address format. Limited to 3 keys for standardness. Superseded by P2SH.
P2SHPay‑to‑Script‑HashApr 2012173,80516Base583Complex scripts (multisig, timelocks) as hash. Script revealed only when spending.
P2SH‑P2WPKHNested‑SegWitAug 2017481,824141Base583P2WPKH wrapped in P2SH for backwards compatibility. Works with older wallets.
P2SH‑P2WSHNested‑SegWit‑ScriptAug 2017481,824141Base583P2WSH wrapped in P2SH. Complex scripts with SegWit benefits, backwards compatible.
P2WPKHPay‑to‑Witness‑Pubkey‑HashAug 2017481,824141Bech32bc1qNative SegWit. Lower fees, fixes malleability.
P2WSHPay‑to‑Witness‑Script‑HashAug 2017481,824141Bech32bc1qNative SegWit for complex scripts. Used for multisig, timelocks with witness benefits.
P2TRPay‑to‑TaprootNov 2021709,632341Bech32mbc1pTaproot with Schnorr signatures and MAST. Best privacy and efficiency.

Example addresses:

  • P2PKH: 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa (Satoshi's address)
  • P2SH / P2SH-P2WPKH: 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy
  • P2WPKH: bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
  • P2TR: bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297

Note: P2PK and P2MS have no address format; they use raw scripts. OP_RETURN outputs are unspendable and used for data embedding (max ~80 bytes).


Script Execution Examples

Each script type has its own execution flow. The unlocking script runs first, then the locking script.

P2PK (Pay-to-Pubkey)

The simplest script: locks directly to a public key.

ComponentScript
Locking<pubkey> OP_CHECKSIG
Unlocking<signature>
StepOperationStack
1Push <signature>[sig]
2Push <pubkey>[sig, pubkey]
3OP_CHECKSIG[1]

P2PKH (Pay-to-Pubkey-Hash)

Most common legacy type. Hides the public key until spending.

ComponentScript
LockingOP_DUP OP_HASH160 <pubkeyhash> OP_EQUALVERIFY OP_CHECKSIG
Unlocking<signature> <publickey>
StepOperationStack
1Push <signature>[sig]
2Push <publickey>[sig, pubkey]
3OP_DUP[sig, pubkey, pubkey]
4OP_HASH160[sig, pubkey, hash160(pubkey)]
5Push <pubkeyhash>[sig, pubkey, hash160(pubkey), pubkeyhash]
6OP_EQUALVERIFY[sig, pubkey] (fails if hashes ≠)
7OP_CHECKSIG[1]

P2MS (Bare Multisig)

Requires m-of-n signatures. Example: 2-of-3.

ComponentScript
LockingOP_2 <pubkey1> <pubkey2> <pubkey3> OP_3 OP_CHECKMULTISIG
UnlockingOP_0 <sig1> <sig2>
StepOperationStack
1Push OP_0 (dummy)[0]
2Push <sig1>[0, sig1]
3Push <sig2>[0, sig1, sig2]
4Push OP_2[0, sig1, sig2, 2]
5Push pubkeys[0, sig1, sig2, 2, pk1, pk2, pk3]
6Push OP_3[0, sig1, sig2, 2, pk1, pk2, pk3, 3]
7OP_CHECKMULTISIG[1](verifies 2 valid sigs from 3 keys)

Note: OP_0 is required due to an off-by-one bug in the original implementation.

P2SH (Pay-to-Script-Hash)

Hides complex scripts behind a hash. Example wrapping a 2-of-3 multisig:

ComponentScript
LockingOP_HASH160 <scripthash> OP_EQUAL
UnlockingOP_0 <sig1> <sig2> <redeemScript>
StepOperationStack
1Push sigs + redeemScript[0, sig1, sig2, redeemScript]
2OP_HASH160[0, sig1, sig2, hash160(redeemScript)]
3Push <scripthash>[0, sig1, sig2, hash160(redeemScript), scripthash]
4OP_EQUAL[0, sig1, sig2, 1] (if hashes match)
5Execute redeemScript[1](multisig verification)

P2WPKH (Native SegWit)

Like P2PKH but witness data is separate. Smaller, cheaper.

ComponentScript
LockingOP_0 <20-byte-pubkeyhash>
Witness<signature> <publickey>

Execution is similar to P2PKH, but signature/pubkey are in the witness (not scriptSig), so they don't count toward the base transaction size.

P2WSH (Native SegWit Script)

Like P2SH but with witness. Example: 2-of-3 multisig.

ComponentScript
LockingOP_0 <32-byte-scripthash>
WitnessOP_0 <sig1> <sig2> <witnessScript>

The witness script is hashed with SHA256 (not HASH160) and verified, then executed.

P2TR (Taproot)

Two spending paths: key path (single sig) or script path (MAST).

ComponentScript
LockingOP_1 <32-byte-tweaked-pubkey>
Witness (key path)<schnorr-signature>

Key path: Just provide a Schnorr signature. Looks identical to single-sig regardless of hidden scripts.

Script path: Reveal the script and Merkle proof. Complex conditions stay hidden unless used.


Locking Mechanisms

Bitcoin Script supports various ways to lock funds with different spending conditions.

Lock TypeOpcodeUse CasesScript Pattern
Time Lock (Absolute)OP_CHECKLOCKTIMEVERIFYEscrow, inheritance, vesting<time> OP_CLTV OP_DROP <pubkey> OP_CHECKSIG
Time Lock (Relative)OP_CHECKSEQUENCEVERIFYPayment channels, delayed spending<blocks> OP_CSV OP_DROP <pubkey> OP_CHECKSIG
Multi-SignatureOP_CHECKMULTISIGShared custody, corporate walletsOP_2 <pk1> <pk2> <pk3> OP_3 OP_CHECKMULTISIG
Hash LockOP_HASH256Atomic swaps, HTLCsOP_HASH256 <hash> OP_EQUALVERIFY <pubkey> OP_CHECKSIG
ConditionalOP_IF/OP_ELSEMultiple spending paths, refundsOP_IF <path1> OP_ELSE <path2> OP_ENDIF
Data EmbedOP_RETURNData storage, coin burningOP_RETURN <data>

Combined Locks

Locks can be combined for complex conditions. Example: time-locked multisig with escape clause:

OP_IF
  <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
  OP_2 <pubkey1> <pubkey2> <pubkey3> OP_3 OP_CHECKMULTISIG
OP_ELSE
  <pubkey4> OP_CHECKSIG
OP_ENDIF