What The Heck is Script

Rajarshi Maitra
BitHyve
Published in
12 min readMay 5, 2020
-------------Kernel------------
* Inside Input and Output structures
* Basic of Script mechanism
* Step by step of P2PK
* Step by step of P2PKH
* Glimpse at advanced contracting in Bitcoin

Script is a programming language designed along with the Bitcoin protocol by Satoshi Nakamoto. Unlike traditional programming language, it doesn’t have formal grammar and syntax instead, its a simple list of operations, and their definitions are written in the same C++ source code as the Bitcoin Protocol. The Script programming language is used for verifying transactions in the network. But this simple and primitive programming language opens up the doors for the possibility of creating Smart Contracts. The concept of smart contract was first envisioned and the term was coined by famous cypherpunk-polymath Nick Szabo in 1997, and was independently conceived from the idea of cryptocurrencies.

A smart contract is a computer protocol intended to digitally facilitate, verify, or enforce the negotiation or performance of a contract. Smart contracts allow the performance of credible transactions without third parties. These transactions are trackable and irreversible. — wikipedia

Satoshi’s design of Script in 2008 immediately reminded people of this old idea. Even Satoshi while designing Bitcoin had the idea of adding programmability into the currency. That’s why he added many script operations in the first version of the codebase, way more than it’s required to simply perform transaction validations.

Simply put, a script is a program that gets executed every time a node receives a transaction from the network. Every transaction has an associated script, and every node in the network, upon receiving a transaction, executes the Script in their own local machine. Because of the mechanisms of Proof Of Work, once these transactions get added in the blockchain they become immutable, thus every node always computes a script in the exact same steps and arrives at the exact same result. A combination of such programming language with an immutable database creates two fundamentally exciting phenomenon, one is economical and another is of computer science:

  • We get a digital currency with attached programs, thus a “programmable money”.
  • We get a distributed computational network where arbitrary programs can get executed inside different machines to result in the exact same output and they cannot be censored.

The implication of these two concepts is phenomenal and can be argued to have the potential to fundamentally change the mechanism of the world financial system and distributed censorship-resistant computation. In this article, we peel off the Bitcoin system to look deep inside the scripting mechanism and we start by looking inside a transaction Input and Output structure.

If you are unfamiliar with the basic structural building blocks of Bitcoin, I highly recommend going through my previous blog post on UTXO first before proceeding further.

Inputs and Outputs

To quickly revisit, every transaction has a list of Inputs and Outputs. The output contains a lock and an amount (in sats), the input contains a pointer to a past output and an unlocking key. Scripting is all about this locking and unlocking mechanism.

An Input and Output structure looks as below:

Input and Output structure

The input structure contains Prev TxID and TxIndex, which together points back to previous output that this input is attempting to unlock. It contains a key (ScriptSig) that successfully unlocks that previous output.

The output structure contains a lock (ScriptPubkey) and an amount in satoshis that is locked inside this output.

Only the owner of the ScriptPubkey (an entity who holds the corresponding private key) can create a correct ScriptSig that opens the lock.

This ScriptSig and ScriptPubkey are simple data field that contains serialized information (sequence of 0s and 1s). Upon receiving a transaction, a node first extracts the corresponding ScriptSigs and ScriptPubkeys from the input and output lists (outputs are from past transactions). After retrieving the raw data, The ScriptSig and ScriptPubkey information are concatenated together.

This full sequence of <ScriptSig><ScriptPubkey>, when parsed by the node reveals the complete Script program, which then the node knows how to execute step by step.

Don’t worry if it sounds all too complicated, we will look in detail into this mechanism.

Script

Script is stack-based, forth like, turing incomplete programming language, written in reverse polish notation.

That is a mouthful. But in essence the concept of Script is quite simple. Script works with a stack-like structure. Think of a stack like a bucket where dishes are placed one after another. The first dish to go inside the bucket will be the last dish to come out and vice versa. This is what is meant by a “first in last out” stack structures. The general-purpose computers also work in this simple stack-based model and Assembly is the language to write programs with such stacks. Script is the Assembly for Bitcoin.

The Script Language

The Script starts working with an empty stack. Data can be pushed into or popped out of the stack.

A Script program consists of two types of objects.

  • OP_CODES: These are specific bytes that represent a certain operation, like Addition, Subtraction, Multiplication, etc.
  • DATA: Everything that is not an OP_CODE is interpreted as raw data, and pushed on top of the stack.

A Script is nothing but a sequence of such OP_CODES and DATA. The execution counter starts at the very first item and then moves along one by one. If the execution counter points to a data, it is pushed inside the stack, else if it is an OP_CODE, data is popped out of the stack and the OP_CODE operation takes place with the popped data. An OP_CODE can pop one or multiple data (dishes) from the stack, but it will always pop from the top. The operations and number of data pops for each OP_CODE is defined inside the Bitcoin protocol. A Complete list of all the currently supported and unsupported OP_CODES with their meaning are listed here.

If a Script evaluation is successful, the top stack element will 1. If any other value remains in the stack top other than 1 , the script evaluation is considered failed.

When a node receives a new transaction from the network, it extracts the ScriptSig and ScriptPubkey fields, concatenate them together to get the final script <ScriptSig><ScriptPubkey>, and attempt the execution starting with an empty stack. If at the end of execution the top stack element is 1, the transaction is considered valid, and the node propagates the transaction to its peers. Else the transaction is considered invalid, and the node doesn’t send it further. Thus invalid transactions never get propagated into the network. Thus each node works as the gatekeeper of the sanity of the entire network.

Script execution in a nutshell

Next we will look into some rudimentary bitcoin scripts and the steps of execution to better understand the mechanism.

Pay To PubKey (P2PK)

This is the most simplest possible Bitcoin script that uses only two data and one op_code. The earliest version of Bitcoin used this type of scripts, and they were used to pay funds directly to a public key. Thus the name, PayToPubKey, or P2PK scripts. Because of their naive approach they are not used anymore as they directly expose the receiver’s public key and are deemed insecure. But it is the best place to start understanding the script execution.

P2PK Script

The ScriptPubkey is the data structure that defines kind of a lock, and the ScriptSig is the data structure that defines the unlocking key. ScriptSig is present inside the transaction input, and ScriptPubkey is extracted from the previous transaction output that this input attempts to unlock.

“Unlocking” is the process of concatenating ScriptSig and ScriptPubkey, interpreting it as a Script program, and upon execution leaves 1 as the top stack element.

The ScriptPubkey and ScriptSig fields for a P2PK type transaction are shown above.

A full P2PK script consists of two data elements, signature and public key, and one op_code OP_CHECKSIG, which checks whether the signature is valid for the given public key. If valid, it return 1 as top stack element, else 0.

Below we show the execution step by step. Follow the red pointer and the corresponding stack items.

Step 1: Stack is Empty, the pointer is at signature

Step 2: signature is data, so its pushed into the stack

Step 3: public key is also data, so its pushed into the stack

Step 4: OP_CHECKSIG is an op_code which takes top two items from the stack, considering the 1st aspublic key and 2nd as signature, and attempts signature verification as per the ECDSA algorithm. If success puts 1 into stack, else puts 0.

(To see the full details of ECDSA verification checkout my Schnorr signature blog post.)

Step 5: Assuming successful ECDSA verification, and 1 in the stack, the transaction is verified and the UTXO is unlocked.

Once a script execution succeeds, the transaction is deemed valid. The previous UTXO gets consumed, and new UTXOs are created as per the output list of this spending transaction. This is what happens when one “spends” bitcoin under the hood.

That’s all for every kind of script execution. The process remains the same, only the complexity of the script program increases from here.

Now that we have seen the most rudimentary script execution of Bitcoin, let’s look at a little evolved version of the same mechanism.

PayToPubKeyHash (P2PKH)

A more complex version of the above P2PK mechanism is P2PKH, where the ScriptPubkey does not include the private key, but a hash ( Hash 1 in the figure below)of the public key. Thus the UTXO does not reveal the receiver’s public key.

The ScriptSig and the ScriptPubkey for a P2PKH script is shown below.

Unlike P2Pk, the signature and the public key both are included in the ScriptSig section. The ScriptPubkey has many more op_codes than P2PK, and a data object Hash 1, which is the hash of the receiver’s public key. While sending bitcoins the sender sends to this public key hash, thus the name PayToPubkeyHash.

As before, we go through the steps of the above script execution below.

Step 1: Pointer is at signature, the stack is empty

Step 2: signature is simple data, so pushed onto the stack, pointer moves to the next element.

Step 3: public key is also data, so pushed on to the stack. The pointer moves to the next element.

Step 4: OP_DUP is an op_code that duplicates the top stack element. after execution, now we have two copies of the same public key on the stack.

Step 5: OP_HASH160 is an op_code that hashes the top stack element. The top public key is hashed into Hash 2.

Step 6: Hash 1 is data, so pushed onto the stack

Step 7: OP_EQUALVERIFY is an op_codes that checks if two stack elements are the same. If they are the same, then remove them and proceed forward. Else, fails the execution.

If the right public key is provided, Hash 2 and Hash 1 will be equal to each other. So they are removed.

Now the stack will be exactly equal to the Step 3 of P2PK execution.

Step 8: OP_CHECKSIG checks the ECDSA algorithm for public key and the signature is the stack. If the correct signature is provided, it will return an 1 onto the stack.

The stack ends with 1 at the end of the execution signifying successful verification. Thus the UTXO is unlocked.

P2PKH is similar to P2PK in essence that it checks a public key against a provided signature, but does that in a roundabout way which provides better privacy.

Thus we have covered the two basic forms of script execution in the Bitcoin system. There can be many other variety of scripts, and one can even cook up their own script. If you want to try out your own custom scripts without the trouble of running bitcoin core in regtest mode, btcdeb is a simple command-line tool built for such purpose.

Smart Contracts

A smart contract is a program that executes in an automated and uncensorable way. Because each of the script of a transaction is independently executed by each node in the network, there is no central server to attack in order to stop the execution.

The ScriptPubkey in every UTXO defines a form of mathematical puzzle, that can be solved by appending the correct ScriptSig. Any valid form of such a puzzle with the right ScriptSig will do the trick, and it doesn’t have to be standard P2PK or P2PKH type of scripts. For example, look the ScriptPubkey and ScriptSig below:

ScriptPubkey: 2 2 OP_ADD OP_EQUAL

ScriptSig: 4

A UTXO with the above ScriptPubkey can be opened with the ScriptSig and will result in 1 on the stack after execution. Trying playing the execution in your head, or use btcdeb.

In this way, a UTXO (or a box full of stats) can define any form of “contract” (puzzle), that when provided with the valid unlocking clause will move the locked bitcoins. Only a valid unlocking clause can move the coins and nothing else. The execution of the contract is guaranteed by the Bitcoin network and we do not need a courtroom to enforce the correct execution between parties. This opens up the door for all possible financial contract designs and started the era of “Smart Contract” since 3rd Jan 2009. People have imagined all sorts of exotic contracting clauses since then.

Conclusion

Bitcoin scripting language was the first glimpse of a decentralized program execution mechanism that initiated the idea of deployable smart contracts. Bitcoin scripting is not at all exotic in terms of programmability and its quite hard to write custom smart contracts of arbitrary complexity. Imagine trying to write an App using Assembly language. One major deficiency is Scripts are turning incomplete, which means it cannot jump to execution statements and cannot run loops. Without turning completeness generalized programs of arbitrary complexity cannot be written using scripts.

But this shortcoming is more of a feature than a bug. More complex a smart contract, the higher the chance of having a bug in the code. And a bug in smart contracting logic is way more catastrophic than regular software bugs. Checkout the DAO hack in Ethereum to get an idea of how devastating a contract bug can be. Also contracts become increasingly difficult to review with increasing complexity.

In this sense, Bitcoin smart contracts are robust due to its absolute simplicity. And yet they are immensely powerful. Even without using loops all sorts of complex logics are possible to design using Bitcoin scripts.

Lightning network is nothing but a very elaborate Bitcoin script known as HTLC (Hashed Time-Locked Contracts). Just by clever use of scripting logic people have devised an entire 2nd layer on top of Bitcoin protocol to facilitate instant zero fees offchain transactions. A HTLC ScriptPubkey looks as below:

OP_IF
[HASHOP] <digest> OP_EQUALVERIFY OP_DUP OP_HASH160 <seller pubkey hash>
OP_ELSE
<num> [TIMEOUTOP] OP_DROP OP_DUP OP_HASH160 <buyer pubkey hash>
OP_ENDIF
OP_EQUALVERIFY
OP_CHECKSIG

Thus, though very simple, Bitcoin scripting is an immensely powerful financial engineering tool that is here to shift the world of finance from the hands of centralized gatekeepers to a decentralized autonomous protocol, executing in an immutable way, available for free for anyone in need.

I hope this explainer article sheds enough light on the elusive world of Bitcoin Scripts and Smart Contracts in general.

Off to the moon!!

--

--

Rajarshi Maitra
BitHyve

Independent Bitcoin and Lightning Network observere learner and educator.