Locking and Unlocking Scripts in Bitcoin

In this article, we learn about locking and unlocking scripts and how they are used in Bitcoin transactions.

Table of contents.

  1. Introduction.
  2. Locking Scripts.
  3. Unlocking Scripts.
  4. The Transaction Execution Stack.
  5. Summary.
  6. References.


In Bitcoin, Transaction scripts are used to infinite loops and secure the network from attacks such as DDoS attacks by limiting the scope of execution. These scripts are made intentionally made turing complete as a security feature. On the other hand, Bitcoin script limitations hinder programmers from creating blockchain applications that support complex transactions.
The Ethereum blockchain supports transaction complexity by enabling developers to code complex application functionality in relation to transactions using a high-level programming language such as solidity while maintaining similar security features.
Transaction scripts are stateless, meaning that before they are executed, the state is either nonexistent or is saved after execution. This ensures that all data that is required for execution is packaged within the script.

In this article, we learn about the two most important transaction scripts namely, the locking and unlocking scripts, and how they are used to make sure that transactions on the blockchain are valid.
Bitcoin transactions take an existing UTXO as input and unlock it using a digital signature to generate a new UTXO and locking it to the new owners' wallet address.

These scripts are coded using Bitcoin's script programming language.

Locking Scripts.

These are scripts that specify a condition that is to be met before funds can be spent on a transaction output by defining a set of operations that should end in the successful execution of the script.
They are also referred to as scriptPubKey. Take the case where A sends B funds, A's transaction data has a locking script embedded in it. This script states that anyone who wants to spend X amounts of bitcoins should have;
(a). A public key that when doubly hashed matches the recipient's wallet address and,
(b). A valid digital signature whose validity can be confirmed by the doubly hashed the public key.
Locking scripts are executed after the execution of unlocking scripts otherwise if they were executed after unlocking scripts, they could replace values on the stack with success values.

Examples of standard locking scripts include;
Pay to Public Key(P2PK)
After the unlocking script pushes a signature onto the stack, this script checks if the signature is valid, if so the outputs can be spent. Operations involved with this script include the following;
OP_DATA_X - adds the recipient's compressed or uncompressed public key to the stack.
OP_CHECKSIG - compares the public key at the top of the sack with the signature below it on the stack.

Pay to Public Key Hash(P2PKH)
Works the same as the previous script but instead of pushing the public key onto the stack, it pushes its hash.
Hashing reduces risks associated with the use of plain text.
For the recipient to be able to spend outputs locked with this script, the unlocking script should push a digital signature followed by the public key that corresponds to the private key that created the signature.
If the public key hash and the signature are valid, the output can be spent.
Examples of OP_CODES associated with this script include;
OP_DUP - copies the value at the top of the stack.
OP_HASH160 - Performs a SHA-256 and RIPEMD-160 double hashing of the copied value.
OP_DATA_X - Pushes the address onto the stack.
OP_EQUALVERIFY* - Verifies that the hash of the copied value matches the expected hash pushed onto the stack.
OP_CHECKSIG - Verifies that stack has a public key and a signature and verifies the signature is valid for the corresponding public key.

Pay to Script Hash(P2PH)
This script requires the recipient to include specific operations in the unlocking script. This is achieved by ending the unlocking script by making it push data onto the stack. When the data is verified to match the script hash, this internal script - redeem script is executed on the pre-locking-script execution stack.
If the redeem completes execution successfully, outputs can be spent otherwise, they remain locked.

Examples of OP_CODES used with this script include;
OP_HASH160 - hashes the data on top of the stack(script to be executed).
OP_DATA_X - pushes the expected redeem script hash.
OP_EQUAL - Verifies the hash of the provided script matches the expected hash.

Multisig (P2MS)
These allow wallets to have multiple private keys that have to sign transactions together otherwise a transaction remains invalid.
An unlocking script in this case should provide the required number of signatures that are checked against public keys. If they are valid, the outputs can be spent.

Commonly used OP_CODES with this script include;
OP_X - pushes the number of signatories onto the stack.
OP_DATA_X - pushes 1 or more public keys to the stack.
OP_CHECKMULTISIG - checks for signatures matching the number of signatories verifying that they correspond to the public keys and making sure the signatures are valid.

Data Output
These are used to create outputs that can't be spent. Instead, they are used to add data to a transaction. Outputs in this case are made unspendable when a failing script is executed. These outputs usually have zero satoshis.

Commonly used OP_CODES used with this script include;
OP_RETURN - used to fail an execution immediately.

Unlocking Scripts.

This script is used to satisfy the condition specified by the locking script. B uses this script to be able to spend the UTXOs sent to him/her. Unlocking scripts are also referred to as scriptSig because they contain a digital signature. Digital signatures are provided by a recipient's wallet address.

For Bitcoin peer nodes to validate a transaction, they execute both the locking and unlocking scripts together. This validation process is as follows; the unlocking script is first copied, then the UTXO that references the input is retrieved, this UTXO consists of a locking script. Both scripts are executed in a sequence. We say the input is valid if the unlocking script satisfies the locking script conditions.
Note that a transaction can have multiple inputs, in this case, all inputs are validated independently, this is part of the overall validation of a transaction. Valid transactions satisfying the output conditions are considered 'spent' and removed from the UTXO set.

The following is a demonstration of locking and unlocking scripts in a bitcoin transaction;


Above we have the resulting script which is a combination of both the locking and unlocking scripts before they are validated.

The Transaction Execution Stack.

The Bitcoin script programming language is stack-based meaning that it only has two operations push and pop. The former adds data on top of the stack while the latter removes data from the top of the stack. This mechanism is referred to as LIFO whereby the Last In is the First Out.
Data is pushed onto the stack, and opcodes are used to either pop a single or multiple parameters from the stack, process them, and occasionally push the result back onto the stack.

Opcodes can also be conditional whereby a condition is first evaluated before an action is taken. For example, the opcode OP_EQUAL is used to pop two items from the stack and then push a TRUE value if the values are equal and FALSE otherwise. A use case is in the validation of a transaction, TRUE is returned if the transaction was indeed a valid transaction.


Bitcoins on the blockchain are not moved from wallet to wallet like cash changing ownership in a transaction, instead, coins are locked and unlocked. In a transaction, the only thing that is changed is the person who can the bitcoins.
A transaction is said to be valid if the result on top of the stack is TRUE({0x01})) or any non-zero value and invalid if the value is FALSE or execution terminates as a result of an opcode such as OP_VERIFY or OP_RETURN of OP_ENDIF among others.


Bitcoin Script
Locking and Unlocking Scripts