Programmable Money
The power of Bitcoin's Script
July 06, 2024 - 1843 words - 10 mins Found a typo? Edit meBitcoin is often referred to as “programmable money” because it allows for the execution of programmable transactions through its scripting language.
Understanding Programmable Money
Programmable money is the ability to embed logic and conditions into financial transactions. This programmability allows transactions to be executed automatically based on predefined rules without intermediaries or manual intervention. It transforms money from a static medium of exchange into a dynamic tool capable of executing complex agreements and automating financial operations.
The Script Language
Bitcoin’s programmability is powered by its built-in scripting language, Script. Unlike traditional programming languages, Script is a stack-based, Forth-like language explicitly designed for Bitcoin transactions.
It supports multi-signature, time-locked, and other conditional transfers that can be programmed into Bitcoin transactions. It is intentionally not Turing-complete, without loops.
This video includes examples of the most commonly used locking/unlocking scripts.
Key Features of Bitcoin’s Script
Stack-Based Execution
Script operates on a stack-based execution model where commands and data are pushed onto a stack and processed in a Last-In-First-Out (LIFO) manner.
Conditional Spending
A transaction that can only be spent if certain data is provided or specific criteria are met. This can be used for:
- escrow services
- atomic swaps
- and other complex financial arrangements
Multisignature
Transactions can be set up to require multiple signatures from different private keys before they can be spent. Useful for:
- joint accounts
- corporate funds
- and enhancing security, as no single party can unilaterally spend the funds
Timelocking
Transactions can include time-based conditions that prevent them from being spent until a certain time or block height is reached. This feature is useful for various purposes:
- delayed payments
- smart contracts
- and ensuring that funds are not spent prematurely (eg: with Lightning Network)
Operation Codes
Bitcoin’s Script performs specific operations within transactions with its OP_Codes. Here are some of them:
OP_DUP
: Duplicates the top stack item.OP_HASH160
: Hashes the top stack item twice (SHA-256 followed by RIPEMD-160).OP_EQUALVERIFY
: Verifies that the top two items are equal and removes them.OP_CHECKSIG
: Verifies a signature against a public key.OP_RETURN
: Marks the transaction output as invalid, often used to store data.
Common Bitcoin address types
In Bitcoin, different address types correspond to various ways to script transactions. Here, we’ll explore examples of Bitcoin Script for each major address type. Each address type has its own specific script format.
- P2PK: Earliest legacy transactions using full public keys directly.
- P2PKH: Begins with
1
. Legacy transactions using public key hashes. - P2SH: Begins with
3
. Legacy, encapsulates complex scripts like multisig. - P2MS: It is typically a type of P2SH or P2WSH address.
- P2WPKH: Begins with
bc1
. Native SegWit, more efficient transactions. - P2WSH: Begins with
bc1
. SegWit for complex scripts. - P2TR: Begins with
bc1p
. SegWit Taproot addresses, improving privacy and efficiency for complex transactions.
Using native SegWit (P2WPKH and P2WSH) is preferable when possible, as it maximizes the benefits of the SegWit upgrade, but P2SH-SegWit can be useful for compatibility with older systems.
P2PK (Pay-to-PubKey) - Earliest Legacy Address up
Before P2PKH and P2SH became standard, Bitcoin addresses were not as flexible or feature-rich. Here are some considerations:
- Addresses starting with 1 but without hashing the public key.
- Not common in modern practice due to lack of privacy and efficiency.
- The P2PK format is largely obsolete in favor of (at very least) P2PKH.
P2PKH (Pay-to-PubKey-Hash) - Legacy Address up
A typical P2PKH script consists of two main parts:
- ScriptPubKey: The locking script (also known as the output script) that specifies how funds can be spent.
- ScriptSig: The unlocking script (also known as the input script) that provides the necessary data to unlock the funds.
ScriptPubKey (Locking Script)
OP_DUP OP_HASH160 <PubKHash> OP_EQUALVERIFY OP_CHECKSIG
OP_DUP
: Duplicates the top stack item (the public key).OP_HASH160
: Hashes the public key with SHA-256 followed by RIPEMD-160.<PubKHash>
: The hashed public key (a 20-byte value).OP_EQUALVERIFY
: Checks if the hashed public key matches the hash in the script.OP_CHECKSIG
: Verifies the provided signature against the public key.
ScriptSig (Unlocking Script)
<sig> <PubK>
<sig>
: The digital signature generated by the private key.<PubK>
: The public key corresponding to the address.
Execution Flow
- The
ScriptSig
(unlocking script) is pushed onto the stack. - The
ScriptPubKey
(locking script) is executed.
P2SH (Pay-to-Script-Hash) up
P2SH scripts are used for more complex scripts. The primary feature is that the address itself encodes a hash of a script, which will be used in the transaction.
ScriptPubKey (Locking Script)
OP_HASH160 <ScriptHash> OP_EQUAL
OP_HASH160
: Hashes the script with SHA-256 followed by RIPEMD-160.<ScriptHash>
: The hashed script (a 20-byte value).OP_EQUAL
: Checks if the hash matches the provided script hash.
ScriptSig (Unlocking Script)
<sig> <PubK> ... <ScriptSig>
<sig>
: The digital signature.<PubK>
: The public key.<ScriptSig>
: The actual script that matches the script hash, which itself will be executed by the Bitcoin network.
P2MS (Pay-to-Multisig) up
Script Format
OP_M <M> <PubK1> <PubK2> ... <PubKN> OP_N OP_CHECKMULTISIG
OP_M
: The minimum number of signatures required.<PubK1>, <PubK2>, ..., <PubKN>
: The public keys involved in the multisignature scheme.OP_N
: The total number of public keys provided.OP_CHECKMULTISIG
: The opcode that verifies the signatures against the provided public keys.
ScriptPubKey (Locking Script)
OP_2 <PubK1> <PubK2> <PubK3> OP_3 OP_CHECKMULTISIG
This script means that any 2 out of 3 provided public keys are required to sign the transaction for it to be valid.
ScriptSig (Unlocking Script)
<sig1> <sig2> ... <sigN> <SerializedScript>
<sig1>, <sig2>, ..., <sigN>
: The public keys.<SerializedScript>
: The serialized script (the same as the locking script but without theOP_M
andOP_N
).
P2WPKH (Pay-to-Witness-Public-Key-Hash) - Segwit up
P2WPKH is a Segregated Witness (SegWit) address type that uses a different scripting format compared to legacy and P2SH addresses. Simplifies transactions by reducing data size and fees compared to legacy formats.
ScriptPubKey (Locking Script)
OP_0 OP_PUSHBYTES_20 <PubKHash>
OP_0
: A single byte (0x00) indicating the version of the script.OP_PUSHBYTES_20
: Pushes 20 bytes (the public key hash) onto the stack.<PubKHash>
: The 20-byte hash of the public key.
Witness Data
For P2WPKH, the unlocking script is not required in the traditional sense (i.e., inside the unlocking script explicitly included in the transaction input). Instead, the unlocking information is provided as part of the witness data in the SegWit transaction format.
<sig> <PubK>
<sig>
: Digital signature for the transaction.<PubK>
: Public key used to generate the public key hash.
P2WSH (Pay-to-Witness-Script-Hash) - Segwit up
ScriptPubKey (Locking Script)
OP_0 OP_PUSHBYTES_32 <ScriptHash>
OP_0
: Indicates a witness version 0 (SegWit).OP_PUSHBYTES_32
: Pushes the next 32 bytes (the script hash) onto the stack.<ScriptHash>
: 32-byte hash of the redeem script.
Witness Data
<sig1> <sig2> ... <RedeemScript>
<sig1>, <sig2>
: Signatures required to unlock the transaction.<RedeemScript>
: The actual script that matches the script hash. This script will be executed as part of the witness data.
P2TR (Pay-to-Taproot) - Taproot up
Taproot combines Schnorr signatures with MAST, enabling private, efficient spending conditions and making complex transactions appear standard unless conditions are revealed. It allows the efficient execution of complex transactions while hiding their details.
ScriptPubKey (Locking Script)
OP_1 <x-only PubK>
OP_1
: Indicates a witness version 1 (Taproot).<x-only PubK>
: A 32-byte Schnorr public key (without the y-coordinate).
Witness Data
<sig>
<sig>
: A Schnorr signature proving possession of the private key corresponding to the x-only pubkey.
*There is an oddity in CHECKMULTISIG execution up
The implementation of OP_CHECKMULTISIG
pops one more item than it should. The extra item is disregarded when checking the signatures, so it has no direct effect on the OP itself. It must be present because if OP_CHECKMULTISIG
attempts to pop on an empty stack, it will cause a stack error and script failure.
OP_0 <sig2> <sig3> 2 <PubK1> <PubK2> <PubK3> 3 OP_CHECKMULTISIG
The input script in this multisig is not <sig2> <sig3>
but OP_0 <sig2> <sig3>
.
It because the custom early on to use OP_0
which later because a relay policy rule and eventually a consensus rule (BIP147).
It is possible that the original developer added the extra element in the original version of Bitcoin, so they could add a feature for allowing a map to be passed in a later soft fork (for performance reasons). However, that feature was never implemented, and the BIP147 update to the consensus rules in 2017 makes it impossible to add that feature in the future.
Only Bitcoin’s original developer could tell whether the dummy stack element was the result of a bug or a plan for a future upgrade. From now on, if you see a multisig script, you should expect to see an extra OP_O
in the beginning, whose only purpose is as a workaround to an oddity in the consensus rules.
Follow-ups
- Official docs of Script in the Bitcoin Wiki.
- Bitcoin IDE is an online Bitcoin Script visual emulator. Great for learning purposes.
- Script Editor is product-driven research on Bitcoin scalability and usability.
- Miniscript is a language for writing (a subset of) Bitcoin Scripts in a structured way, enabling analysis, composition, generic signing, and more.
Related readings
- Mastering Bitcoin by Andreas M. Antonopoulos, David A. Harding