Programmable Money

Programmable Money

A look at the Bitcoin scripting language

A fundamental feature of the Bitcoin network is the ability to transfer value (bitcoin) between participants in the network. Throughout history, the human species has always exchanged value in one way or another but what the Bitcoin network offers us now is the ability to programmatically set what we refer to as spending conditions (a set of requirement(s) that must be met before we can transfer value).

The language for constructing these conditions is the Bitcoin scripting language called Script, and yes the language is called Script.
Bitcoin uses the unspent transaction output (UTXO) model which means all the bitcoin available for potential spending exists as UTXOs. Attached to each UTXO is a script known as a locking script (or scriptPubKey) that describes the condition for spending that UTXO. This is one of the ways Script is used in Bitcoin transactions.

Before we look at some of the other ways Script is used in Bitcoin, let's highlight the features of the language

Features of Script

  • Stack-based: The code runs in a linear format from left to right following the popular stack data structure which uses the LIFO (last in first out) ordering. The execution is similar to the Forth-like programming language used in some calculators.

  • Turing incomplete: Predictable execution time with no looping construct/"goto" functionality. This is to prevent any potential DDoS attack/infinite loop

  • Deliberately simple and limited in scope - Making it able to run in a wide range of hardware devices

  • Mostly used in transaction validation/verification

So how does Script work

Script operates in a reverse polish notation format, which can be illustrated with an example:
The operation 1 + 1 in reverse polish notation will be 1 1 +

To run this operation in a Script, it will involve

  • Pushing the 1 onto the execution stack

  • Pushing the second value 1 into the stack

  • And the next item is the + which is an operator that will accept two values from the top of the stack. The two values are popped off the stack and added together before pushing the resulting value onto the stack

Writing this action in Script will look like this

OP_1 OP_1 OP_ADD

In visual terms

Let's try another example

5 4 OP_ADD 9 OP_EQUAL

Note: 1 = true and 0 = false

This explains the basic operation and concept around Script, as earlier stated Script powers Bitcoin transactions i.e for a transaction to be valid or invalid, the final value of the script execution has to be valid or invalid respectively

Script is mostly made up of operators (op-codes) and conditions, Let's explore some of the use cases of the language in our regular Bitcoin usage.

The major usage is applied in the locking and unlocking script

Locking script - Spending condition(s) placed on an output
Unlocking script - Satisfies the condition(s) specified by the locking script

To validate a Bitcoin transaction the node has to copy the unlocking script and retrieve all the UTXOs referenced by the inputs. The inputs are validated separately by executing the locking and unlocking script in sequence. An input is valid only if the unlocking script satisfies the locking script.

Using a script we have seen before imagine if this is the locking script for an input
5 OP_ADD 9 OP_EQUAL
Can you guess the unlocking script that will result in a valid transaction?
Yes you're right, the answer is OP_4 or 4 (OP can always be omitted)

Even though it is totally possible to have a trivial locking and unlocking script as above, let's dive into one of the most used scripts in the bitcoin network - The Pay To Public Key Hash Script (P2PKH).

Pay To Public Key Hash
In most Bitcoin transactions the outputs contain a locking script that locks the output to a public key hash, more commonly known as a bitcoin address. In this case, the locking script is of the form:

OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG

In the context of an actual transaction i.e if Emeka is sending bitcoin to Tunde

The above locking script can be satisfied with an unlocking script of the form:
<Tunde Signature> <Tunde Public Key>

The result will be TRUE if the unlocking script has a valid signature from Tunde's private key that corresponds to the public key hash set in the locking script.

Combining the scripts and Evaluating we get a valid transaction.

<TundeSig> <TundePubK> + OP_DUP OP_HASH160 <Tunde Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG

Let's visualize the execution of the combined script

Control flow in Scripts

Just like most programming languages, we can perform flow conditions in Scripts too but of course with some differences.
Flow control is implemented using the IF, ELSE, ENDIF, and NOTIF opcodes, boolean operators such as BOOLAND, BOOLOR, and NOT can also be used.

Let's examine the major difference between flow control in a regular language vs Script using pseudocode

Flow control for most languages

if(condition):
 code to run when condition is true
else:
 code to run when condition is false
code to run in either case

Flow control in Script

condition
IF
 code to run when condition is true
ELSE
 code to run when condition is false
ENDIF
code to run in either case

Notice that the condition comes before the IF opcode

Flow controls in Script enable us to provide different execution paths for spending a transaction. Conditions can be nested as much as possible giving room for really complex constructs

IF
 <Emeka's Pubkey> CHECKSIG
ELSE
 <Tunde's Pubkey> CHECKSIG
ENDIF

Looking at the above, it shows a locking script where we have two signers, Emeka and Tunde, and either one is able to redeem the funds.
Also, notice the condition for the IF operator is missing, this is the case because the condition will be passed as part of the unlocking script.

We have been able to see some simplified explanations of Script, But with the presence of various opcodes and unlimited conditional nesting, a lot can and has been constructed using this limited but powerful language.
The lightning network which is a layer 2 Bitcoin solution makes use of really complex scripts using Script to achieve its use cases.

Here is a snippet just for the curious.

# To remote node with revocation key
OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
OP_IF
    OP_CHECKSIG
OP_ELSE
    <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
    OP_NOTIF
        # To local node via HTLC-timeout transaction (timelocked).
        OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
    OP_ELSE
        # To remote node with preimage.
        OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
        OP_CHECKSIG
    OP_ENDIF
    1 OP_CHECKSEQUENCEVERIFY OP_DROP
OP_ENDIF

Conclusion

As we can see the Bitcoin scripting language is really what qualifies bitcoin as programmable money, the examples and Opcode highlighted above are just some of the ways Script can be useful, to see all the available opcodes check out https://en.bitcoin.it/wiki/Script#Opcodes.
With a combination of these opcodes and desired conditions, we can implement different spending options.

You can also find a playground here https://siminchen.github.io/bitcoinIDE/build/editor.html where you can run some Script and visualize its execution in a simulated stack visualizer.

And lastly, I will appreciate any questions or feedback
Remain Curious

References

https://learnmeabitcoin.com/technical/script
https://github.com/bitcoinbook/bitcoinbook

https://learn.saylor.org/course/view.php?id=500