Skip to content

Latest commit

 

History

History
394 lines (244 loc) · 21.6 KB

ch06.asciidoc

File metadata and controls

394 lines (244 loc) · 21.6 KB

Programming Bitcoin

Script

The ability to lock and unlock coins is the mechanism by which we transfer Bitcoin. Locking is leaving some Bitcoins to someone else. Unlocking is spending some Bitcoins that have been left for you.

In this chapter we examine this locking/unlocking mechanism, which is often called a smart contract. Script is what combines what you’ve learned in the first part of the book. Script is the glue that makes transactions work with digital signatures. Script essentially allows people to be able to prove that they have the right to spend certain outputs. We’re getting a little ahead of ourselves, though, so let’s start with how SCRIPT works and go from there.

Mechanics of SCRIPT

If you are confused about what a "smart contract" is, don’t worry. "Smart contract" is a fancy way of saying "programmable" and the "smart contract language" is simply a programming language. SCRIPT is the smart contract langauge, or the programming language used to express the conditions under which bitcoins are spendable.

Think of a personal check. In a sense, a personal check is a type of contract. A personal check is an agreement to transfer some amount of money from one person to another. Bitcoin has the digital equivalent of a contract in SCRIPT.

SCRIPT is a limited programming language in the sense that it doesn’t have certain features. Specifically, it does not have any mechanism for loops and is therefore not Turing complete.

Note
Why Bitcoin isn’t Turing Complete

Turing completeness in a programming language essentially means that you have the ability to do loops. Loops are a useful construct in programming, so you may be wondering at this point why SCRIPT doesn’t allow for loops.

There are a lot of reasons for this, but let’s start with program execution. Anyone can create a SCRIPT program that every full node on the network executes this program. If SCRIPT were Turing Complete, it’s possible for the loop to go on executing forever. This would essentially cause every full node to enter and never leave that loop and would thus be an easy way to attack the network. A single script that has an infinite loop could take down Bitcoin! This would not be good, for obvious reasons and would be a large systematic vulnerabilty. Ethereum, which has Turing Completeness in its smart contract language, Solidity, handles this problem by forcing contracts to pay for program execution with something called "gas". An infinite loop will exhaust whatever gas is in the contract as by definition, it will run an infinite number of times.

There are other reasons to avoid Turing Completeness and that’s because smart contracts with Turing completeness are very difficult to analyze. A Turing Complete smart contract’s execution conditions are very difficult to enumerate and thus easy to create behavior that’s unintended, causing bugs. Bugs in a smart contract mean that it’s vulnerable to being unintentionally spent, which means the contract writer would lose money.

What you are required to do as part of a transaction is to assign Bitcoins to a locking script. The locking script is what’s specified in ScriptPubKey (see chapter 5). Think of this as the lock box where some money is deposited which only the person with the key to the box can open. The money inside, of course, can only be accessed by someone with the key.

The actual unlocking of bitcoin is done in the ScriptSig field (see chapter 5) and proves ownership of the locked box in order to spend the funds.

How SCRIPT works

SCRIPT, as a language, operates by processing one item at a time. There are two possible types of items: elements and operations.

Elements are just data. They are byte strings of length 1 to 75. A typical element might be a der signature or a sec pubkey.

Script Elements
Figure 1. Elements

Operations do something to the data. They consume zero or more elements from the processing stack and put zero or more elements back on the stack.

Script Operations
Figure 2. Operations

A typical operation might be something like OP_DUP, which will duplicate the top element (consuming 0) and putting a new element on top (putting 1).

OP_DUP
Figure 3. OP_DUP duplicates the top element

At the end of processing all the items in the stack, the top element of the stack must be non-zero for the script to execute successfully. Having no elements on the stack or having the top element be zero would result in a failed execution. Failed execution generally means that the transaction which includes the unlocking script is invalid and not accepted on the network.

Example Operations

There are many other operations besides OP_DUP. OP_HASH160 does a sha256 followed by a ripemd160 to the top element of the stack (consuming 1) and putting a new element back (putting 1). Note in the diagram that y = ripemd160(sha256(x))

OP_HASH160
Figure 4. OP_HASH160 does a SHA256 followed by RIPEMD160 to the top element

Another very important operation is OP_CHECKSIG. OP_CHECKSIG consumes 2 elements from the stack, the first being the pubkey, the second being a signature, and examines if the signature is good for the given pubkey. If so, OP_CHECKSIG pushes a 1 onto the stack, otherwise puts a 0 on the stack.

OP_CHECKSIG
Figure 5. OP_CHECKSIG checks if the signature for the pubkey is valid or not

Parsing the script fields

Both ScriptPubKey and ScriptSig are parsed the same way. If the byte is between 0x01 and 0x4B (which we call n), we read the next n bytes as an element. Otherwise, the byte represents an operation, which we have to look up. Here are some operations and their byte codes:

  • 0x00 - OP_0

  • 0x51 - OP_1

  • 0x5F - OP_15

  • 0x75 - OP_DUP

  • 0x93 - OP_ADD

  • 0xa9 - OP_HASH160

  • 0xac - OP_CHECKSIG

There are many more and the full list can be found at http://wiki.bitcoin.it

Coding a Script parser and serializer

Given this rule, we can write a very basic parser. We assume that we have some lookup table that gives us what the name of a particular op code is in OP_CODES.

class Script:

    def __init__(self, items):
        self.items = items  # (1)

    def __repr__(self):
        result = ''
        for item in self.items:
            if type(item) == int:
                result += '{} '.format(OP_CODES[item])
            else:
                result += '{} '.format(item.hex())
        return result

    @classmethod
    def parse(cls, s):
        length = read_varint(s)
        items = []
        count = 0
        while count < length:
            current = s.read(1)
            count += 1
            current_byte = current[0]
            if current_byte >= 1 and current_byte <= 75:  # (2)
                n = current_byte
                items.append(s.read(n))
                count += n
            else:
                op_code = current_byte
                items.append(op_code)
        return cls(items)

    def serialize(self):
        result = b''
        for item in self.items:
            if type(item) == int:
                result += int_to_little_endian(item, 1)
            else:
                length = len(item)
                prefix = int_to_little_endian(length, 1)
                result += prefix + item
        total = len(result)
        return encode_varint(total) + result


OP_CODES = {...}
  1. The items attribute is a list of items in this script. p2pkh (later in this chapter), would be OP_DUP, OP_HASH160, 20-byte hash, OP_EQUALVERIFY, OP_CHECKSIG, or 5 items.

  2. If the byte is between 1 and 75 inclusive, we have an element.

Combining the script fields

It’s important to realize at this point that the lock box (ScriptPubKey) and the unlocking (ScriptSig) are in different transactions. Specifically, the lock box is where the bitcoins are received, the unlocking is where the bitcoins are spent. The input in the spending transaction points to the receiving transaction. Essentially, we have a situation like this:

ScriptPubKey and ScriptSig
Figure 6. ScriptPubKey and ScriptSig

Since ScriptSig unlocks ScriptPubKey, we need a mechanism by which the two scripts combine. What we do in Bitcoin is take the items from ScriptSig and ScriptPubKey and combine them as above. The items from the ScriptSig go on top of all the items from ScriptSig. Each item is processed one at a time until no items are left to be processed or if the script exits early.

There are many types of standard scripts in Bitcoin including the following:

  • p2pk - Pay-to-pubkey

  • p2pkh - Pay-to-pubkey-hash

  • p2sh - Pay-to-script-hash

  • p2wpkh - Pay-to-witness-pubkey-hash

  • p2wsh - Pay-to-witness-script-hash

Addresses are actually compressed ScriptPubKeys. Wallets know how to interpret various address types (p2pkh, p2sh, bech32) and create the appropriate ScriptPubKey. All of the above have a particular type of address format so people can pay to them.

To show exactly how all this works, we’ll next take a look at the original script pay-to-pubkey

p2pk

Pay-to-pubkey (aka p2pk) was used a lot during the early days of bitcoin. Most coins thought to belong to Satoshi are in p2pk outputs. There are some limitations that we’ll discuss below, but let’s first focus on how p2pk works.

We learned back in chapter 3 how signing and verification work in ECDSA. Specifically, you need the message (z), the public key (P) and the signature (r,s). The mechanics of p2pk are simply that you send bitcoins to a public key and let the owner of the private key unlock through a signature and determine where the bitcoins should go. Effectively, the ScriptPubKey puts those bitcoins under the control of the private key owner.

Specifying where the bitcoins go is the job of the scriptPubKey. As stated above, this is the lock box that receive the bitcoins. The actual scriptPubKey looks like this:

P2PK ScriptPubKey
Figure 7. Pay-to-pubkey (p2pk) ScriptPubKey

Note the OP_CHECKSIG, as that will be very important. The ScriptSig is the part that unlocks the received bitcoins. In the case of p2pk, the ScriptSig is just the signature.

P2PK ScriptSig
Figure 8. Pay-to-pubkey (p2pk) ScriptSig

The scriptPubKey and ScriptSig combine to make a processing stack that looks like this:

P2PK Combination
Figure 9. p2pk Combined

The two columns below are Items of Script and the actual stack. At the end of this processing, the top element in the stack must be non-zero to be considered a valid ScriptSig. The script items are processed one item at a time. We start with the items as combined above:

P2PK Start
Figure 10. p2pk Start

The first item is the signature, which is an element. This is data that goes on our stack.

P2PK Step 1
Figure 11. p2pk Step 1

The second item is the pubkey, which is also an element. This is again, data that goes on our stack.

P2PK Step 2
Figure 12. p2pk Step 2

OP_CHECKSIG consumes 2 stack items (pubkey and signature) and determines if they are valid for this transaction. OP_CHECKSIG will put a 1 back if the signature is valid, 0 if not. Assuming that the signature is valid for this public key, we have this situation:

P2PK End 1
Figure 13. p2pk Step 3

We’re finished processing all the items of SCRIPT and we’ve ended with a single item on the stack which is non-zero (1 is definitely not 0). Therefore, this script is valid.

If we were to get an invalid signature, the result from OP_CHECKSIG would be zero, ending our script processing like this:

P2PK End 2
Figure 14. p2pk End

We end with a single item on the stack which is zero. This means the script is invalid and a transaction with this ScriptSig is invalid.

The script will validate if the signature is valid, but fail if the signature is not. Essentially, we are in a situation where the ScriptSig will only unlock the ScriptPubKey if the signature is valid for that public key. In other words, only someone with knowledge of the secret can produce a valid ScriptSig.

Incidentally, we can see here why ScriptPubKey is called ScriptPubKey. The public key in uncompressed SEC format is the main item in ScriptPubKey in p2pk (the other being a OP_CHECKSIG). Similarly, ScriptSig is named as such because p2pk is a single item which is the DER signature format.

Problems with p2pk

Pay-to-pub-key is pretty intuitive in the sense that there is a public key that anyone can send some bitcoins and a signature that can be produced by the owner of the private key to spend that amount. This works well, but there are some problems.

First, the public keys are long. We know from chapter 3 that SECP256K1 public points are 33 bytes in compressed and 65 bytes in uncompressed sec format. Unfortunately, you can’t send the 33 or 65 bytes raw very easily. Most character encodings don’t render certain byte ranges as they are control characters or newlines or similar. The sec format is typically rendered instead in hexadecimal, doubling the length (hex encodes 4 bits per character instead of 8). This makes the compressed and uncompressed formats 66 and 130 characters respectively, which is way bigger than most identifiers. To compound this, early Bitcoin transactions simply didn’t use the compressed versions so the hexadecimal addresses were 130 characters each! This is not fun or easy for people to communicate by email, much less by voice!

Why did Satoshi use the Uncompressed SEC format?

It seems the uncompressed SEC format doesn’t make sense for Bitcoin given that block space is at a premium, so why did Satoshi use it? It turns out that Satoshi was utilizing the OpenSSL library to do the SEC format conversions and the OpenSSL library at the time Satoshi wrote Bitcoin (circa 2008) did not support compressed public keys.

Later on, when the compressed SEC format was added to OpenSSL, Bitcoin started using them as well.

Second, because the public keys are long, this causes a more subtle problem. The UTXO set becomes bigger since this large public key has to be kept around and indexed to see if it’s spendable. This may require more resources on the part of nodes.

Third, because we’re storing the public key in the ScriptPubKey field, it’s known to everyone. That means should ECDSA someday be broken, these outputs could be stolen. This is not a very big threat since ECDSA is used in a lot of applications besides Bitcoin and would affect all of those things, too. For example, quantum computing has the potential to break RSA and ECDSA, so having something else in addition to protect these outputs would be more secure.

For these reasons p2pk is considered obsolete.

Solving the problems with p2pkh

Pay-to-pubkey-hash has a bunch of advantages over p2pk:

  1. The addresses are shorter.

  2. It’s protected by ECDSA/SHA256 and RIPEMD160.

Addresses are shorter due to the use of the SHA256 and RIPEMD160 hashing algorithms. We utilize both in succession and call that HASH160. The result of HASH160 is 160-bits or 20 bytes, which can be encoded into an address.

The actual result is an address that you may have seen on the Bitcoin network, something that looks like this:

1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH

This address actually has within it the 20 bytes in hex that look like this:

751e76e8199196d454941c45d1b3a323f1433bd6

These 20 bytes are the result of doing a HASH160 operation on this (compressed) SEC public key:

0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798

p2pkh

Pay-to-pubkey-hash (aka p2pkh) was used during early days of bitcoin, though not nearly as much as p2pk.

Once again, the lockbox where the bitcoins go is the job of the ScriptPubKey. The actual ScriptPubKey looks like this:

P2PKH ScriptPubKey
Figure 15. Pay-to-pubkey-hash (p2pkh) ScriptPubKey

Note that OP_CHECKSIG is still here and OP_HASH160 makes an appearance. Also note that the sec pubkey has disappeared and has been replaced by a 20 byte hash. There is also a new op code that you haven’t seen before, OP_EQUALVERIFY.

The ScriptSig, or the unlocking part of the script looks like this:

P2PKH ScriptSig
Figure 16. Pay-to-pubkey-hash (p2pkh) ScriptSig

As in p2pk, the ScriptSig has the DER signature. Unlike p2pk, however, the ScriptSig now also has the SEC pubkey. In essence, the pubkey has moved from ScriptPubKey to ScriptSig.

The ScriptPubKey and ScriptSig combine to make a processing list of items that need processing that looks like this:

P2PKH Combination
Figure 17. p2pkh Combined

At this point, the script is processed one item at a time. We start with the items as above.

P2PKH Start
Figure 18. p2pkh Start

The first two items are elements, so they go straight on the stack.

P2PKH Step 1
Figure 19. p2pkh Step 1

OP_DUP duplicates the top element, so we end up with this:

P2PKH Step 2
Figure 20. p2pkh Step 2

OP_HASH160 will take the top element and perform the HASH160 operation on it (sha256 followed by ripemd160), creating a 20-byte hash like so:

P2PKH Step 3
Figure 21. p2pkh Step 3

The next item on the stack is an element, thus goes straight on the stack.

P2PKH Step 4
Figure 22. p2pkh Step 4

We are now at OP_EQUALVERIFY. What this op code does is it consumes the top two elements and sees if they’re equal. If they are equal, then the script processing proceeds. If they are not equal, the script stops immediately and is considered invalid. We assume here that they’re equal, leading to this:

P2PKH Step 5
Figure 23. p2pkh Step 5

We are now at exactly where we were in during the OP_CHECKSIG part of processing p2pk. Once again, we assume that the signature is valid:

P2PKH End
Figure 24. p2pkh End

There are two ways this script can fail. If you provide a public key that does not HASH160 to the 20-byte hash in the ScriptPubKey, the script will fail at OP_EQUALVERIFY. The other fail condition is if you do provide the right public key, but an invalid signature. That would end the script with a 0 at the end, failing the script.

This is why we call this type of script pay-to-pubkey-hash. The ScriptPubKey has the 20-byte hash of the public key and not the public key itself. We are locking Bitcoins to a hash of the public key and are responsible for revealing the public key as part of spending the output in our ScriptSig.

The major advantage is that the ScriptPubKey is shorter (just 25 bytes) and a hacker would not only have to solve the Discrete Log problem in ECDSA, but also figure out a way to find pre-images of both RIPEMD160 and SHA256. The latter condition, incidentally, is not known to be quantum vulnerable. That is, there is no known quantum algorithm for creating a hash pre-image that’s better than a conventional computer.

Scripts can be arbitrarily constructed

Note that scripts can essentially be anything. Script is a smart contract language and you can express the conditions under which the bitcoins can be unlocked in any manner that you wish. The one limitation is that you can’t use loops (Turing Completeness, remember?) Here is an example ScriptPubKey:

Example 1 ScriptPubKey
Figure 25. Example ScriptPubKey

Here’s a ScriptSig that will unlock the above.

Example 1 ScriptSig
Figure 26. Example ScriptSig

The combination will look like this:

Example 1 Combination
Figure 27. Example Combined

This is how the script processing will start:

Example 1 Start
Figure 28. Example Start

OP_4 will put a 4 on the stack

Example 1 Step 1
Figure 29. Example Step 1

OP_5 will likewise put a 5 on the stack.

Example 1 Step 2
Figure 30. Example Step 2

OP_ADD will consume the top two items of the stack, add them together and put back the sum.

Example 1 Step 3
Figure 31. Example Step 3

OP_9 will put a 9 on the stack

Example 1 Step 4
Figure 32. Example Step 4

OP_EQUAL will consume 2 items and put a 1 back if equal, 0 back if not.

Example 1 End
Figure 33. Example End

Note that this isn’t particularly hard to figure out and requires no signature. As a result, this sort of script is vulnerable to being taken by pretty much anyone. Think of this as a lock box with a very flimsy lock that anyone can break into. It turns out that most transactions have some signature component in them as a script without some signature component is very easily stolen.

Exercise 1

Create a ScriptSig that can unlock this ScriptPubKey

Exercise 1
Figure 34. Exercise 1

Utilty of Scripts

The previous exercise was a bit of a cheat as OP_MUL is no longer allowed on the Bitcoin network. Version 0.3.5 of Bitcoin disabled a lot of different OP codes as anything that had even a little bit of potential to create vulnerabilties on the network were disabled. The main culprits were a couple of severe bugs related to OP_LSHIFT and OP_RETURN.

This is just as well since most of the functionality in SCRIPT is actually not utilized very much. From a software maintainence standpoint, this is not a great situation as the code has to be maintained despite its lack of usage. This is why Bitcoin is moving more towards simplifying the smart contract language and not expanding it. This is a way to make Bitcoin more secure.

This is in stark contrast to other projects which try to expand their smart contract languages.

Exercise 2

Figure out what this script is doing:

Exercise 2
Figure 35. Exercise 2

SHA1 Piñata

In 2013, Peter Todd created a script very similar to the exercise above and put some Bitcoins into it to create an economic incentive for people to find hash collisions. The donations reached 2.49153717 BTC and when Google actually found a hash collision for SHA1 in February of 2017, this script was promptly redeemed. The transaction output was 2.48 coins which was $2848.88 USD at the time.

Conclusion

We’ve covered SCRIPT and how it works. We can now proceed to the actual creation and validation of transactions.