Lecture 8: Cryptocurrency Features

Announcements

  • Homework 3 is due on Saturday 2/15 at 10pm
  • Test 1 is next Tuesday 2/18 during discussion lab
  • Today’s office hours will start at 3:45pm (rather than 3:30pm), still in CDS 1324

Learning outcomes

By the end of today’s lecture, you will learn:

  • How light clients can partially check the validity of the Bitcoin blockchain.
  • How pseudorandom generation allows Bitcoin users to generate many keys from a single one.
  • How to run computer scripts in Bitcoin, and in another cryptocurrency called Ethereum.

Review from last lecture

There are three types of participants within Bitcoin.

  • A light client only needs to hold one or more secret keys for a digital signature scheme, plus the block headers.
  • A node additionally stores the entire state of the blockchain.
  • A miner additionally tries to write new blocks on the blockchain.

Nodes and miners in Bitcoin maintain a local copy of the blockchain, and they can check the validity of each new transaction and block that is gossiped around the peer-to-peer network.

Bitcoin transactions and blocks eventually become finalized, meaning that they are common knowledge to all honest participants. To achieve consensus, Bitcoin requires the following assumption:

The majority of the computational power used for mining (at any given time) is controlled by honest miners.

More formally, Bitcoin achieves the following two security goals:

  1. Liveness: Every transaction is eventually finalized by all honest nodes.
  2. Safety: Honest nodes do not finalize different blocks at the same height.

8.1 Simplified Payment Verification

Note that we have not yet provided any security guarantees to the light clients, since they cannot independently validate transactions and blocks. Let’s see if we can extend liveness and safety to them.

  • Remember that a light client doesn’t have the hard drive space to store the 500+ gigabytes of data in the Bitcoin blockchain.
  • Still though, they can periodically connect to the Internet and download some data.

It would be great if we could compress all transactions into a small dataset. Unfortunately, that’s impossible. Instead, we can leverage the facts that:

  1. The nodes know the full list of transactions, and
  2. The nodes have good Internet connectivity, so they are available to provide transaction data to the light clients.

The clients just need to verify the accuracy of the transactions – that is, to make sure that the nodes are telling them the truth. To do that, we can use a Merkle tree.

Refresher on Merkle trees

Remember what Merkle trees do:

  • We can create a small representation of an entire set of data \(S = (f_1, f_2, \ldots, f_k)\).
  • Later, we can open any element of the set and prove set membership.

In more detail: we construct a Merkle tree as follows.

  • Take the hash of each individual file \(f_i\).
  • Then, take the hash of each pair of hashes.
  • Only store the hash at the root of the tree.

Generating a Merkle proof (source: Alin Tomescu, Decentralized Thoughts blog)

If you store one 32-byte hash digest corresponding to the set of files \(f_1, f_2, \ldots, f_k\), then someone else can prove to you that one file (say, \(f_3\)) is contained within the set \(S\) represented by the Merkle tree.

Verifying a Merkle proof (source: Alin Tomescu, Decentralized Thoughts blog)

Application to SPV

How does this relate to Bitcoin? Well, if you think about the transactions in blocks as “files,” we can do something similar here.

To support light clients, we are going to make two changes to the way that Bitcoin works. The first change applies to everyone, and the second change applies only to light clients.

  1. Each block will contain the Merkle root of all transactions within the block. The block itself will also contain all transactions, as before.

    We will call the block header as just the following items: the nonce of the current block, the root of a Merkle tree for all transactions within that block, and the pointer to the previous block’s hash. Because the Merkle root binds together all of the transactions, it now suffices to create a proof-of-work puzzle where the hash is only taken over the header, and not the transactions themselves.

    Merkle tree in the block header (image source: Nakamoto whitepaper)
  2. A light client will follow the same rule as nodes: find the blockchain of the largest height (or more accurately, the most cumulative difficulty). But the light client only downloads the block headers, not the full blocks (i.e., they won’t know the transactions).

To give a sense of scale:

  • Each header is 80 bytes in size, so downloading all block headers for Bitcoin is about 70 MB of data right now.
  • This is much smaller than 500+ GB for the full blocks!

With this info, a light client can:

  • Verify the proof of work puzzles themselves.
  • Query a full node to ask whether they have received money. That is: if the client believes it has received money in block \(N\), it can ask a node to send (1) the transaction that pays its public key and (2) a Merkle proof that it is contained within the set represented by that block’s Merkle root.

Note that a light client cannot verify:

  1. That the full block is valid (in particular, that the transactions have valid signatures)
  2. That their money hasn’t yet been spent in a subsequent block.

They must rely on the nodes to perform verification check #1, and on their own memory for property #2.

This makes light clients more vulnerable to attackers than nodes. For instance, a malicious miner can just create a single “bad” block that contains invalid signatures; nodes will reject it but a light client will be fooled.

For this reason, light clients should be even more careful when finalizing a transaction.

Eventually though, light clients will inherit the liveness and safety guarantees from the nodes.

8.2 Pseudorandom Generation

Next, let’s explore in detail one point that I briefly mentioned when defining light clients.

A light client only needs to hold one or more secret keys.

Why might you want to hold more than one signing key in Bitcoin?

Let’s consider the following scenario: suppose you are Alice, and you already have some Bitcoins. This means that you already have created secret/public keys for a digital signature scheme.

Then you have two interactions throughout the day.

  • In the morning, you visit a coffee shop and pay its store owner Bob in Bitcoin. This requires posting your public key and a digital signature to the globally-viewable blockchain.

  • In the afternoon, you talk to your friend Carol, and she decides to send you some money. To do so, she’s asking you to give her a public key that she can (hash and) use as a destination address for the transaction.

What should you tell Carol?

You already have the secret/public keys from your old wallet. It’s perfectly acceptable to reuse that one.

But there is a downside: now the entire world will see that the same person who went to the coffee shop in the morning is the person who is receiving coins right now. That is: the two transactions would be linkable.

A better approach is to run KeyGen again and generate a fresh secret/public key pair for this new transaction. This would provide some measure of pseudonymity.

We will discuss the level of anonymity that Bitcoin achieves – or fails to achieve – in a future lecture. But still, this is better than nothing.

Signing keys are quick and easy to create. But if you do this every day, quickly you will find yourself needing to remember a lot of signing keys, and that sounds cumbersome and error-prone. How can we make your life easier?

Quality of life improvement #1: generating many keys from one

At its core, the problem here is that Alice is generating many signing keys at random. They have no connection to each other, so she needs to store them all.

On the other hand, randomness was essential for the digital signature scheme to work. It is crucial that everyone chooses the secret key from a very large space, so that an attacker cannot brute force it. If everyone picked the same secret key for instance, then the signature scheme falls apart.

So here we reach a conundrum: we want a process that Alice can use to reliably and deterministically produce secret keys, but also one that “looks random” to an outsider.

Question. Is there anything in our cryptographic toolbox that allows us to do this?

A cryptographic hash function \(H\) provides this kind of property. Here’s one way that we can leverage it.

Suppose we have one single main key \(m\) that we ask Alice to memorize. She can use it to generate many child keys pseudorandomly.

  • For her morning interaction with Bob, she can use \(H(m, 0)\) to generate the first secret signing key (and then calculate the public key from it using the digital signature scheme’s key generation algorithm).
  • For her afternoon interaction with Carol, she can use \(H(m, 1)\) to generate the second signing key.

This idea is effective thanks to the properties of the underlying hash function.

  • Due to preimage resistance, even if one signing key happens to be compromised and exposed to the world, nobody can use it to reverse-engineer the main key \(m\).

  • Also, the rows of the truth table of \(H\) appear to be independent. So the two signing keys – and their associated public keys – have no discernible connection to each other.

We can take this idea one step further: from the main key \(m\), we can generate several keys for different cryptocurrencies like Bitcoin, Ethereum, ZCash, and more. Then we can apply the previous idea to those keys in order to generate many secret/public key pairs.

This is the basic idea of a Bitcoin Improvement Protocol, or BIP, called hierarchical deterministic wallets.

With HD wallets, you only have to remember the main key \(m\), from which you can (re)generate as many signing keys for as many cryptocurrencies as you want.

BIP-32 standard for hierarchical deterministic wallets (image source: BIP-32 specification)

Some important caveats before we continue!

  • The actual function you need to run here is not quite \(H\), but rather a slightly more complicated construction called HMAC applied to the hash function.
  • And there’s more work required to go from the hash digest to something that has the structure of a digital signing key.
  • And there are even more subtle details that are very important in practice, but out of scope for this lecture.

Remember: always make sure to use well-written crypto software that is developed by people who have studied these subtle details. Don’t roll your own crypto software in practice!

Quality of life improvement #2: a human-memorable seed

We have made Alice’s life somewhat easier: now all she has to do is remember the 32 byte main key. It might look something like this:

from os import urandom
from binascii import hexlify

main_key = urandom(32)
print("Base 16 output:", hexlify(main_key))

from base64 import b64encode
print("Base 64 output:", b64encode(main_key))
Base 16 output: b'd70ee60ddbceae19032cc3c9a219b68883ad6666899a9ff3521eb3d2d7a3c92e'
Base 64 output: b'1w7mDdvOrhkDLMPJohm2iIOtZmaJmp/zUh6z0tejyS4='

These kinds of string encodings:

  • Are incredibly useful for computer programs to ingest, process, and output printable characters for other computer programs to use.
  • But Alice is a person. This style of encoding is not meant for her. What we need is a better way to encode 256 bits of random data, but in a way that makes sense for people to remember or write down.

xkcd password strength comic (image source: xkcd.com)

This technique uses the same principle that we discussed in Lecture 1:

all information is data, and all data can be encoded into bits.

Specifically, let’s think about the ways that we can store 12 bits worth of data.

  • We can write the data in binary, for example \(0101 1010 0011\).
  • We can write the data as hex characters as \(6a4\).
  • We can write the data in base 64 as “Wj”

Or we can choose any other representation that gives us \(2^{12}\) possible values that the data can take… perhaps one that is even easier for humans to remember.

There is another Bitcoin Improvement Protocol standard made for this purpose: creating human-memorable seeds. It defines a set of \(2^{11} = 2048\) words and records every 11 bits of the main key using one word.

Let’s look at its set of words. I’ll use English in this example, although other languages are specified in the standard too.

# source: https://github.com/trezor/python-mnemonic

# pip install mnemonic
from mnemonic import Mnemonic
mnemo = Mnemonic("english")
print(mnemo.wordlist[:10])  # first ten words
print(mnemo.wordlist[-10:]) # last ten words
['abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse']
['yard', 'year', 'yellow', 'you', 'young', 'youth', 'zebra', 'zero', 'zone', 'zoo']

We can create Alice’s main key by sampling 24 of these words at random, each of which encodes 11 bits of data. So collectively, they encode the 256 bits of entropy that we need.

words = mnemo.generate(strength=256)
print(words)

seed = mnemo.to_seed(words, passphrase="")
print("\nBase 64 encoding:", b64encode(seed))
script frog armed oyster salmon crop horse strong cereal regular tenant royal deliver gate rather nephew own truck inmate avoid weekend allow wife crew

Base 64 encoding: b'5Xsx/kM1DgcdnBFjxan9w8ribFMRLkd7iYNiZARP97RYUGCFH6eu/abIKa4zR2peuyQWxktTgxdcrWqzBpWqCg=='

Caution: this code is just for demonstration purposes. Do NOT actually use this seed phrase to hold any money!

8.3 Scripts on the Blockchain

So far, we have said that a transaction has the form “Alice pays 1 coin to Bob.”

Actually, this is not quite true. Bitcoin allows for more complicated transactions than this.

A Bitcoin transaction is actually a computer script that looks like this.

Script in a Bitcoin transaction

In words, a Bitcoin transaction involves Alice posting her own public key and the hash of Bob’s public key. This hash is called Bob’s wallet “address.” The transaction can be thought of as a computer program that says:

“For anyone who shows up later with a public key and claims to be the recipient of this transaction, take the hash of their public key and check if it equals 8ec...b1. If so, give my coins to that person.”

In fact, there are several valid operations that can be performed in a Bitcoin transaction. However, the scripting language is not Turing-complete; that is, you can’t write any computer program.

Relatedly, this is why some Bitcoin transactions take up more space than others: they can be more complicated computer programs.

Transaction sizes in a Bitcoin block

Ethereum

Ethereum is another cryptocurrency. It was created in 2013 – so, four years after Bitcoin, but still awhile ago. Ethereum takes the idea of scripting much further than Bitcoin does.

The Ethereum blockchain is essentially a global computer where everyone agrees on the state of this computer. The global state is replicated on all nodes that are in the Ethereum network. The Ethereum state can only be changed by users issuing transactions.

Similarities. Ethereum shares several features in common with Bitcoin, such as:

  • a peer-to-peer network that connects users,
  • a consensus algorithm to agree on state updates, and
  • using cryptographic primitives like digital signatures and hashes.

Additionally, Ethereum also has its own digital currency, known as Ether.

Just like Bitcoin transactions:

  • Ethereum transactions are grouped into blocks and broadcasted into the network.
  • Any person can use the global computer by issuing transactions and paying the required fees in its Ether currency.
  • Every few seconds a leader gets elected, and that leader chooses which transactions go in the next block.

Differences. However, Ethereum’s primary objective is not to function as a digital currency payment network, and Ether is not intended to be a conventional currency. Instead,

  • Ether is designed to be a utility currency that pays for the use of the Ethereum world computer.
  • Transactions on the Ethereum blockchain explain how to move from one state of the machine to the next.

Source: Ethereum docs

Also, Ethereum’s scripting language is Turing-complete, unlike the one in Bitcoin. So, it can operate as a general-purpose computer and execute code with more complexity.

You can think of Ethereum as a state machine. Ethereum’s state is a large data structure which holds not only all accounts and balances, but a machine state, which can change from block to block according to a pre-defined set of rules, and which can execute arbitrary machine code.

The specific rules of changing state from block to block are defined by the Ethereum Virtual Machine (EVM). Every transaction causes the EVM to run which causes the global state to change. The order of the transaction is very important! The EVM is single threaded.

Source Ethereum docs

Smart Contracts

Why use a bunch of computers in order to create one global computer?

One benefit is that you can write software that makes and enforces financial decisions. In other words, you can create a contract that can be enforced via computer code.

For example, you could post a Sudoku puzzle to Ethereum and create a smart contract that says:

“I will pay 1 coin to the first person who posts a solution to the Sudoku puzzle in the next 5 minutes. Otherwise, I will reclaim my money.”

You must currently own 1 coin for this smart contract to be valid. If so, your coin is “locked” in the smart contract until it gets paid out (either to the puzzle-solver, or back to you).

The smart contract itself must be written as a computer program. Ethereum supports several programming languages; a popular one is a language called Solidity that was developed for Ethereum. Solidity is an object oriented imperative programming language with a syntax similar to Python and C++.

Going one step further: you can even create new coins on top of Ethereum!

  • Ethereum contains a standard interface for fungible tokens on the Ethereum blockchain, called ERC20. It is a set of rules that define how a token contract should behave and interact with other contracts and wallets on the Ethereum network.

  • For instance, you could create a new kind of coin called Sudokoin and say that anyone who solves a Sudoku puzzle earns 1 Sudokoin. Here is open-source Solidity code to do exactly this! (Whether or not this coin has any value is a different matter…)

If you think that the idea of blending “computer code” with “irreversible financial transactions” can lead to disaster, then you would be right!

Bugs in Ethereum smart contracts can lead to your money being sent to the wrong place, or being locked forever. For this reason, smart contracts should be (and sometimes are) carefully checked by human code reviewers and formally verified by computers.

8.4 The Path Forward

This brings us to the end of Unit 1 of this course. In this unit, you have seen how to:

  • Define the security guarantees of digital signatures and hash functions. Explain their role in authenticating people and data.
  • Describe the goal of decentralization and distributed consensus, in the context of financial payments via cryptocurrencies.
  • Determine if a potential transaction is valid. Explain the mining algorithm and its role to incentivize honest participation.

Importantly: agreement is possible even though the Bitcoin participants do not have a common communication channel where one person posts a message and everyone can see it.

Instead, they merely need to ‘gossip’ messages they’ve heard over the network. Even though some people in the network might be lying, as long as enough of the computing power in the network is honest, then everyone eventually reaches agreement over the same set of facts.

The idea that a bunch of computers over the Internet can reach agreement in the face of an adversary is kind of amazing! It provides an elegant solution to integrity and availability problems in a variety of applications that go beyond money:

  • Sending a message to your friends and making sure that all of them hear it, and that everyone has the common knowledge that everyone else has heard it too. This is called the broadcast problem.

  • Conducting an election or any other decision-making process where it is important to make sure that everyone has consensus on which decision has been selected. This is called the Byzantine agreement problem.

  • Designing a high-assurance computer server that continues to work even if some of the individual CPUs become corrupted or faulty. This is called Byzantine fault-tolerance.

We will talk about these applications in Unit 2 of the course.

Also, the Bitcoin blockchain has (at least) two major issues that we will come back to after spring break.

  1. Privacy. In Bitcoin, all transactions are public knowledge. Even though your name is not published on the blockchain directly, it may be possible to trace the owner of each account. We discussed the limitations of pseudonymity in today’s coffee shop example, and we will discuss privacy in more detail in Unit 3 of the course.

  2. Energy inefficiency. Bitcoin’s proof-of-work puzzle solves the agreement problem, but it is incredibly computationally intensive. The U.S. Department of Energy released a report last year that estimated that “electricity use from cryptocurrency mining probably represents from 0.6% to 2.3% of U.S. electricity consumption” (source).

The good news is that there are other ways to solve the agreement problem that do not require this kind of computing power.

We will talk about both of these scientific issues, and how to resolve them, in more detail in Unit 4 of this course.

Finally, we will have a broader discussion of the impact of crypto and cybersecurity technology on society, and laws and policy regulations, in the final unit of the course.