As I was learning about how Bitcoin works under the hood, I was pretty surprised to find out that Bitcoin has smart contracts, at least to a limited extent. Though they are fairly simple, these powerful contracts are what allow for the security of Bitcoin transactions. It’s also important to understand how these scripts work for understanding what Taproot does.
Transactions In Bitcoin Are Spending Conditions Being Set and Fulfilled
Learning about Bitcoin transactions can be a bit confusing at first, so let’s start with the blockchain, where all the transactions are stored. The blockchain is a data structure which keeps track of our transactions like a digital ledger. Each block in this blockchain contains a number of transactions. The question is, what do these transactions look like? Often when initially explaining how a blockchain works, we may make it seem like it is a bunch of balances and pseudo-anonymous addresses to whom those funds belong to. In order to understand how transactions really work in Bitcoin, we’ll have to leave this impression behind.
Let’s talk about what a single Bitcoin transaction looks like. Every transaction can have multiple inputs and multiple outputs. A transaction output only comprises of an amount of bitcoin(in satoshis) and the conditions under which this bitcoin can be spent(called a locking script).
There are two types of transaction outputs: Unspent Transaction Outputs(UTXOs) and Spent Transaction Outputs(STXOs). A UTXO is a transaction output whose conditions haven’t been satisfied yet. These conditions can only be satisfied once, because once they are the transaction output is no longer a UTXO, but an STXO that can no longer be spent.
Transaction inputs are how UTXOs are spent. Transaction inputs contain the transaction id of the UTXO they are spending, an index which denotes which UTXO of that transaction they are spending(because, remember, a transaction can have multiple outputs with different locking scripts), and an unlocking script. The unlocking script’s purpose is just to satisfy the conditions previously set by the locking script they are trying to spend.
So your balance is not stored on the blockchain along with your Bitcoin address, just multiple UTXOs which are spendable by the same private key. A Bitcoin wallet can calculate your balance by scanning the UTXOs and seeing which ones can be redeemed by your private key. The sum of these is your balance.
Let’s dig deeper into how the locking and unlocking scripts work along with different types of locking scripts.
Bitcoin Transactions Are Written In A Scripting Language Called Script
This language is different from most programming languages that we work with these days. Though it may seem a bit odd and basic at first, like many other things when it comes to Bitcoin, it has very good reasons for it’s unique design. The two key distinctions between Script and other languages are the following:
- Isn’t Turing complete
- Uses a stack data structure
A Turing complete language is a language which can express any possible program. This is also seen as being able to solve any possible problem. The catch here is that you can never know in advance when the program will terminate. A Turing complete language includes loops which could run for large amounts of time. So while it can solve any possible problem, it assumes infinite memory and you can’t know the runtime in advance.
Having a Turing complete scripting language would be problematic for Bitcoin because every node in the Bitcoin network needs to be able to validate every transaction and a predictable execution time is necessary. So, Script isn’t Turing complete. It mainly contains conditional If…Then…Else statements, and doesn’t have any loops or recursion that could introduce unpredictable runtimes.
Additionally, Bitcoin uses a stack, a kind of data structure where you can only add onto the top, and only remove from the top. It’s called a First In, Last Out(FILO) method of accessing objects. You can add to a stack using the “push” operator, which adds an object to the top of the stack, and you can remove objects using the “pop” operation which removes the object on the top of the stack.
While this does make for a primitive language in terms of functionality, it successfully fits the specialized requirements of Bitcoin. For example, in a more widely-used programming language, like Python, adding two numbers together will usually look something like this:
1 + 1
This would return 2. Pretty straightforward, right?
With Script, this would look like:
OP_1 OP_1 OP_ADD
1 is pushed onto the stack, then another 1 is pushed onto the stack. Next, the operator “ADD”, which pops two values from the stack, adds them, and then pushes their result onto the stack, is run. The only value left on the stack is 2, and the program terminates.
Types of Transaction Scripts
When it comes to Bitcoin transactions, there are different types of transaction scripts. The most common is Pay-To-Public-Key-Hash(P2PKH), which looks like this:
OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG
And can be spent with this unlocking script:
<signature> <public key>
So, how are these scripts run and validated? If a new transaction is being created which takes the locking script shown above and uses the following unlocking script to spend it(as an input for it’s transaction) a node can verify it by running the unlocking and locking scripts together. The node copies the unlocking script from the transaction input, copies the locking script from the UTXO referenced by the input, and then the unlocking and locking scripts are executed sequence, so that it looks like this:
<signature> <public key> OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG
To break it down, there are two key parts of this combined script. The first is ensuring that the public key provided by the unlocking script has a public key hash equal to the public key hash the locking script has specified(a public key hash is equal to the RIPEMD160 hash of the SHA-256 hash[when both of these hashes are used in this order it is also referred to as HASH160] of the public key).
The second key part is verifying the signature using “OP_CHECKSIG” which takes the signature and corresponding public key as inputs and pushes a true or false value to the stack.
Another type of common transaction script in Bitcoin is Pay-to-Script-Hash(P2SH), which allows for diverse transaction formats. With P2SH, there is a locking script, unlocking script, and a redeem script. The redeem script is the script which specifies the conditions required to be fulfilled in order to spend the allotted amount of Bitcoin. For this examples, let’s say that this is our redeem script:
<3> <Public Key 1> <Public Key 2> <Public Key 3> <Public Key 4> <4> OP_CHECKMULTISIG
OP_CHECKMULTISIG allows us to have multisig transactions. These are transactions with numerous valid public keys who can spend the transaction, along with a specified number of distinct signatures needed to successfully spend it. In the above redeem script, there are 4 valid signers of the transaction, and only 3 transactions are needed for the funds to be spent. This makes it something called a 3-of-4 multisig.
The locking script on the other hand, doesn’t contain these conditions. Instead, it is a script with the HASH160 of the redeem script. It looks something like this:
OP_HASH160 <hash of redeem script> OP_EQUAL
This locking script says that the unlocking script must provide the redeem script, which is then hashed to confirm that the same conditions that were set are being fulfilled, along with actually fulfilling the conditions set by the redeem script. The UTXO doesn’t contain the redeem script, just it’s hash, hence the name “Pay-to-Script-Hash”.
The unlocking script looks like this:
<Signature 1> <Signature 2> <Signature 3> <<3> <Public Key 1> <Public Key 2> <Public Key 3> <Public Key 4> <4> OP_CHECKMULTISIG>
This looks long, but it is essentially just providing the redeem script along with the signatures required in order to have it result in TRUE, thus allowing you to spend the transaction.
This would mean that running the unlocking script with the locking script would look like:
<Signature 1> <Signature 2> <Signature 3> <<3> <Public Key 1> <Public Key 2> <Public Key 3> <Public Key 4> <4> OP_CHECKMULTISIG> OP_HASH160 <hash of redeem script> OP_EQUAL
Here’s the breakdown of how it works:
P2SH are more flexible that P2PKH because there are many possibilities for what the redeem script can look like.
Though not as flexible as Ethereum smart contracts written in Solidity, which are turing complete and far more general purpose, Bitcoin’s limited smart contracts allow for simple and somewhat complex transactions with an emphasis on security!