If you have any questions or comments about the following tutorial, make sure to join our Telegram chat. Ask us anything!
With all the recent craze in Bitcoin and Ethereum mining it’s easy to wonder what the fuss is all about. For newcomers to this space, they hear wild stories of people filling up warehouses with GPUs making millions of dollars worth of cryptocurrencies a month. What exactly is cryptocurrency mining? How does it work? How can I try coding my own mining algorithm?
We’ll walk you through each of these questions in this post, culminating in a tutorial on how to code your own mining algorithm. The algorithm we’ll be showing you is called Proof of Work, which is the foundation to Bitcoin and Ethereum, the two most popular cryptocurrencies. Don’t worry, we’ll explain how that works shortly.
What is cryptocurrency mining?
Cryptocurrencies need to have scarcity in order to be valuable. If anyone could produce as many Bitcoin as they wanted at anytime, Bitcoin would be worthless as a currency (wait, doesn’t the Federal Reserve do this? *facepalm*). The Bitcoin algorithm releases some Bitcoin to a winning member of its network every 10 minutes, with a maximum supply to be reached in about 122 years. This release schedule also controls inflation to a certain extent, since the entire fixed supply isn’t released at the beginning. More are slowly released over time.
The process by which a winner is determined and given Bitcoin requires the winner to have done some “work”, and competed with others who were also doing the work. This process is called mining, because it’s analogous to a gold miner spending some time doing work and eventually (and hopefully) finding a bit of gold.
The Bitcoin algorithm forces participants, or nodes, to do this work and compete with each other to ensure Bitcoin aren’t released too quickly.
How does mining work?
A quick Google search of “how does bitcoin mining work?” fills your results with a multitude of pages explaining that Bitcoin mining asks a node (you, or your computer) to solve a hard math problem. While technically true, simply calling it a “math” problem is incredibly hand-wavy and hackneyed. How mining works under the hood is a lot of fun to understand. We’ll need to understand a bit of cryptography and hashing to learn how mining works.
A brief introduction to cryptographic hashes
One-way cryptography takes in a human readable input like “Hello world” and applies a function to it (i.e. the math problem) to produce an indecipherable output. These functions (or algorithms) vary in nature and complexity. The more complicated the algorithm, the harder it is to reverse engineer. Thus, cryptographic algorithms are very powerful in securing things like user passwords and military codes.
Let’s take a look at an example of SHA-256, a popular cryptographic algorithm. This hashing website lets you easily calculate SHA-256 hashes. Let’s hash “Hello world” and see what we get:
Try hashing “Hello world” over and over again. You get the same hash every time. In programming getting the same result again and again given the same input is called idempotency.
A fundamental property of cryptographic algorithms is that they should be extremely hard to reverse engineer to find the input, but extremely easy to verify the output. For example, using the SHA-256 hash above, it should be trivial for someone else to apply the SHA-256 hash algorithm to “Hello world” to check that it indeed produces the same resultant hash, but it should be very hard to take the resultant hash and get “Hello world” from it. This is why this type of cryptography is called one way.
Bitcoin uses Double SHA-256, which is simply applying SHA-256 again to the SHA-256 hash of “Hello world”. For our examples throughout this tutorial we’ll just use SHA-256.
Now that we understand what cryptography is, we can get back to cryptocurrency mining. Bitcoin needs to find some way to make participants who want to earn Bitcoin “work” so Bitcoins aren’t released too quickly. Bitcoin achieves this by making the participants hash many combinations of letters and numbers until the resulting hash contains a specific number of leading “0”s.
For example, go back to the hash website and hash “886”. It produces a hash with 3 zeros as a prefix.
But how did we know that “886” produced something with 3 zeros? That’s the point. Before writing this blog, we didn’t. In theory, we would have had to work through a whole bunch combinations of letters and numbers and tested the results until we got one that matched the 3 zeros requirement. To give you a simple example, we already worked in advance to realize the hash of “886” produced 3 leading zeros.
The fact that anyone can easily check that “886” produces something with 3 leading zeros proves that we did the grunt work of testing and checking a large combination of letters and numbers to get to this result. So if I’m the first one who got this result, I would have earned the Bitcoin by proving I did this work — the proof is that anyone can quickly check that “886” produces the number of zeros I claim it does. This is why the Bitcoin consensus algorithm is called Proof-of-Work.
But what if I just got lucky and I got the 3 leading zeros on my first try? This is extremely unlikely and the occasional node that successfully mines a block (proves that they did the work) on their first try is outweighed by millions of others who had to work extra to find the desired hash. Go ahead and try it. Type in any other combination of letters and numbers in the hash website. We bet you won’t get 3 leading zeros.
Bitcoin’s requirements are a bit more complex than this (many more leading zeros!) and it is able to adjust the requirements dynamically to make sure the work required isn’t too easy or too hard. Remember, it aims to release Bitcoin every 10 minutes so if too many people are mining, it needs to make the proof of work harder to compensate. This is called adjusting the difficulty. For our purposes, adjusting the difficulty will just mean requiring more leading zeros.
So you can see the Bitcoin consensus algorithm is much more interesting than just “solving a math problem”!
Now that we have the background we need, let’s build our own Blockchain program with a Proof-of-Work algorithm. We’ll write it in Go because we use it here at Coral Health and frankly, it’s awesome.
Before proceeding, we recommend reading our original blog post, Code your own blockchain in less than 200 lines of Go! It’s not a requirement but some of the examples below we’ll be running through quickly. Refer to the original post if you need more detail. If you’re already familiar with this original post, skip to the “Proof of Work” section below.
We’ll have a Go server, where for simplicity we’ll put all our code in a single
main.go file. This file will provide us all the blockchain logic we need (including Proof of Work) and will contain all the handlers for our REST APIs. This blockchain data is immutable; we only need
POST requests. We’ll make requests through the browser to view the data through
GET and we’ll use Postman to
POST new blocks (
curl works fine too).
Let’s start with our standard imports. Make sure to grab the following packages with
github.com/davecgh/go-spew/spew pretty prints your blockchain in Terminal
github.com/gorilla/mux a convenience layer for wiring up your web server
github.com/joho/godotenv read your environmental variables from a
.env file in your root directory
Let’s create a
.env file in our root directory that just stores one environment variable that we’ll need later. Put one line in your
Make your package declaration and define your imports in
main.go in your root directory:
If you read our original article, you’ll remember this diagram. Blocks in a blockchain are verified by comparing the previous hash in a block against the hash of the previous block. This is how the integrity of the blockchain is preserved and how a malicious party can’t change the history of the blockchain.
BPM is your pulse rate, or beats per minute. We’ll be using the beats per minute of your pulse as the data we put in our blocks. Just put two fingers on the inside of your wrist and count how many times you get a beat in a minute, and remember this number.
Some basic plumbing
Let’s add our data model and other variables we’ll need under our imports in
difficulty is a constant that defines the number of 0s we want leading the hash. The more zeros we have to get, the harder it is to find the correct hash. We’ll just start with 1 zero.
Block is the data model of each block. Don’t worry about
Nonce, we’ll explain that shortly.
Blockchain is a slice of
Block which represents our full chain.
Message is what we’ll send in to generate a new
Block using a
POST request in our REST API.
We’re declaring a
mutex that we’ll use later to prevent data races and make sure blocks aren’t generated at the same time.
Let’s quickly wire up our web server. Let’s create a
run function that we’ll call from
main later that scaffolds our server. We’ll also declare our routes handlers in
makeMuxRouter(). Remember, all we need are
GET to retrieve the blockchain, and
POST to add new blocks. Blockchains are immutable so we don’t need to edit or delete.
httpAddr := os.Getenv("ADDR") will pull port
:8080 from our
.env file we created earlier. We’ll be able to access our app through
http://localhost:8080 in our browser.
Let’s write our
GET handler to print our blockchain to our browser. We’ll also add a quick
respondwithJSON function that gives us back error messages in JSON format if any of our API calls produces an error.
Remember, if we’re going a bit too quickly, please refer to our original post that explains each of these steps in more detail.
Now we write our
POST handler. This is how we add new blocks. We make a
POST request using Postman by sending a JSON body e.g.
http://localhost:8080 with your pulse rate you took earlier.
mutex lock and unlock. We need to lock it before writing a new block, or else multiple writes will create a data race. The perceptive reader will notice the
generateBlock function. This is our key function that will handle our Proof of Work. We’ll get to it shortly.
Basic blockchain functions
Let’s wire up our basic blockchain functions before we hit Proof of Work. We’ll add a
isBlockValid function that makes sure our indices are incrementing correctly and our
PrevHash of our current block and
Hash of the previous block match.
We’ll also add a
calculateHash function that generates the hashes we need to create
PrevHash. This is just a SHA-256 hash of our concatenated index, timestamp, BPM, previous hash and
Nonce (we’ll explain what that is in a second).
Let’s get to the mining algorithm, or Proof of Work. We want to make sure the Proof of Work is complete before we’ll allow a new
Block to get added to the
blockchain. Let’s start off with a simple function that checks if the hash generated during our Proof of Work meets the requirements we set out.
Our requirements will be as follows:
- The hash that is generated by our Proof of Work must start with a specific number of zeros
- The number of zeros is determined by the constant
difficultythat we defined at the start of our program (in our case, 1)
- We can make the Proof of Work harder to solve by increasing the difficulty
Write up this function,
Go provides convenient
HasPrefix functions in its
strings package. We define the variable
prefix as a repeat of zeros defined by our
difficulty. Then we check if the hash starts with those zeros and return
True if it does and
False if it doesn’t.
Now let’s create our
We create a
newBlock that takes the hash of the previous block and puts it in
PrevHash to make sure we have continuity in our blockchain. Most of the other fields should be apparent:
Timestampis the string representation of the current time
BPMis your pulse rate you took earlier
Difficultyis simply taken from the constant at the top of our program. We won’t use this field in this tutorial but it’s useful to have if we’re doing further verification and we want to make sure the difficulty is consistent with the hash results (i.e. the hash result has N prefixed zeros so Difficulty should also equal N, or the chain is compromised)
for loop is the critical piece in this function. Let’s walk through what’s happening here:
- We’ll take the hex representation of
Nonceequal to it. We need a way to append a changing value to our hash that we create from our
calculateHashfunction so if we don’t get the leading number of zeros we want, we can try again with a new value. This changing value we add to our concatenated string in
calculateHashis called a “Nonce”
- In our loop, we calculate the hash with
iand Nonce starting at 0 and check if the result starts with the number of zeros as defined by our constant
difficulty. If it doesn’t, we invoke the next iteration of the loop with an incremented Nonce and try again.
- We added a 1 second sleeper to simulate taking some time to solve the Proof of Work
- We keep looping until we get the number of leading zeros we want, which means we’ve successfully completed our Proof of Work. Then and only then do we allow our
Blockto get added to
We’re done writing all our functions so let’s complete our
main function now:
godotenv.Load() we load up our environment variable, which is just the
:8080 port we’ll access from our browser.
A go routine creates our genesis block since we need to supply our blockchain with a starting point.
We fire up our web server with our
run() function we created earlier.
Here’s the finished code in full.
Let’s give this baby a try!
Start up your program with
go run main.go
http://localhost:8080 in your browser:
Our genesis block has been created for us. Now open Postman and let’s send a
POST request to the same route with our BPM (pulse rate) we took earlier in a JSON body.
After we send the request watch what’s happening in your terminal. You’ll see your machine chugging away creating new hashes with incrementing Nonce values until it finally finds a result that has the required number of leading zeros!
When the Proof of Work is solved we get a helpful message saying
work done! and we can check the hash to see that it indeed starts with the number of zeros we set in
difficulty. This means that in theory, the new block we tried to add with BPM = 60 should now have been added to our blockchain.
Let’s refresh our browser and check:
SUCCESS! Our second block has been added to our genesis block. This means we successfully sent our block in a
POST request, which triggered the mining process and ONLY when the Proof of Work was solved did it get added to our blockchain!
Great job! What you just learned is a really big deal. Proof of Work is the foundation to Bitcoin, Ethereum and many of the biggest blockchain platforms around. What we just went through is not trivial; while we used a low difficulty for demonstration purposes, increasing the difficulty to a high number is exactly how production-ready Proof of Work blockchains work.
Now that you intimately understand a key component of blockchain technology, where you go from here is up to you. We recommend the following:
- Learn how blockchain networking works in our Networking tutorial
- Learn how to store large files in a distributed manner and communicate with the blockchain in our IPFS tutorial
- Code a simple P2P blockchain in Go.
- Learn about Byzantine fault tolerance, Turing completeness, and other advanced blockchain concepts here.
- Start your own Hyperledger blockchain
- Build a DApp on Hyperledger
If you’re ready to take another leap, try learning about Proof of Stake. While most blockchains use Proof of Work as their consensus algorithm, Proof of Stake is gaining more and more attention. It is widely believed that Ethereum will be migrating away from Proof of Work to Proof of Stake in the future.
Want to see a tutorial on Proof of Work vs. Proof of Stake? See any bugs in the code above? Love what we’re doing? Hate what we’re doing?
To learn more about Coral Health and how we’re using the blockchain to advance personalized medicine research, visit our website.