bitcoin – [RaHa] Programming

Learning the blockchain by coding your own blockchain in GO!

Newcomers to blockchain mining rarely have a fundamental understanding of what exactly mining is and what is involved! You may have heard  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?

These questions and more will be answered in my three part series on blockchain mining which explains how to mine and write the code to mine so that you have total control over the process. The algorithm we’llstart with 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 calledidempotency.

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.

Mining

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”!

Enough background. Let’s get coding!

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.

First, you’ll need a program to code with. I’d suggest getting Visual Studio Code. It’s lightweight (unlike the full Visual Studio), free, and it’s scalable. Best of all, as you use different coding languages, you can import the capability to use them in VS Code easily and quickly.

Next, download Go!.

After installing and configuring Go, we’ll also want to grab the following packages:

go get github.com/davecgh/go-spew/spew

Spew allows us to view structs and slices cleanly formatted in our console. This is nice to have.

go get github.com/gorilla/mux

Gorilla/mux is a popular package for writing web handlers. We’ll need this.

go get github.com/joho/godotenv

Godotenv lets us read from a .env file that we keep in the root of our directory so we don’t have to hardcode things like our http ports. We’ll need this too.

Let’s also create a .env file in the root of our directory defining the port that will serve http requests. Just add one line to this file:

PORT=8080

Create a main.go file. Everything from now on will be written to this file and will be less than 200 lines of code. Let’s get coding!

Imports

Here are the imports we’ll need, along with our package declaration. Let’s write these to main.go

package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/gorilla/mux"
	"github.com/joho/godotenv"
)

Data model

Let’s define the struct of each of our blocks that will make up the blockchain. Don’t worry, we’ll explain what all of these fields mean in a minute.

type Block struct {
	Index     int
	Timestamp string
	UniqueId  int
	Hash      string
	PrevHash  string
}

Each Block contains data that will be written to the blockchain, and represents each case when you took your pulse rate (remember you did that at the beginning of the article?).

  • Index is the position of the data record in the blockchain
  • Timestamp is automatically determined and is the time the data is written
  • UniqueId is any unique 2 digit number you’d like to use
  • Hash is a SHA256 identifier representing this data record
  • PrevHash is the SHA256 identifier of the previous record in the chain

Let’s also model out the blockchain itself, which is simply a slice of Block:

var Blockchain []Block

So how does hashing fit into blocks and the blockchain? We use hashes to identify and keep the blocks in the right order. By ensuring the PrevHash in each Block is identical to Hash in the previous Block we know the proper order of the blocks that make up the

chain.

Hashing and Generating New Blocks

So why do we need hashing? We hash data for 2 main reasons:

  • To save space. Hashes are derived from all the data that is on the block. In our case, we only have a few data points but imagine we have data from hundreds, thousands or millions of previous blocks. It’s much more efficient to hash that data into a single SHA256 string or hash the hashes than to copy all the data in preceding blocks over and over again.
  • Preserve integrity of the blockchain. By storing previous hashes like we do in the diagram above, we’re able to ensure the blocks in the blockchain are in the right order. If a malicious party were to come in and try to manipulate the data (for example, to change our heart rate to fix life insurance prices), the hashes would change quickly and the chain would “break”, and everyone would know to not trust that malicious chain.

Let’s write a function that takes our Block data and creates a SHA256 hash of it.

func calculateHash(block Block) string {
	record := string(block.Index) + block.Timestamp + string(block.UniqueId) + block.PrevHash
	h := sha256.New()
	h.Write([]byte(record))
	hashed := h.Sum(nil)
	return hex.EncodeToString(hashed)
}

This calculateHash function concatenates IndexTimestampUniqueIdPrevHash of the Block we provide as an argument and returns the SHA256 hash as a string. Now we can generate a new Block with all the elements we need with a new generateBlock function. We’ll need to supply it the previous block so we can get its hash and our ID in UniqueId. Don’t worry about the UniqueId int argument that’s passed in. We’ll address that later.

func generateBlock(oldBlock Block, UniqueId int) (Block, error) {

	var newBlock Block

	t := time.Now()

	newBlock.Index = oldBlock.Index + 1
	newBlock.Timestamp = t.String()
	newBlock.UniqueId = UniqueId 
	newBlock.PrevHash = oldBlock.Hash
	newBlock.Hash = calculateHash(newBlock)

	return newBlock, nil
}

Notice that the current time is automatically written in the block with time.Now(). Also notice that our prior calculateHash function was called. PrevHash is copied over from the hash of the previous block. Index is incremented from the Index of the previous block.

Block Validation

Now we need to write some functions to make sure the blocks haven’t been tampered with. We do this by checking Index to make sure they’ve incremented as expected. We also check to make sure our PrevHash is indeed the same as the Hash of the previous block. Lastly, we want to double check the hash of the current block by running the calculateHash function again on the current block. Let’s write a isBlockValid function that does all these things and returns a bool. It’ll return true if it passes all our checks:

func isBlockValid(newBlock, oldBlock Block) bool {
	if oldBlock.Index+1 != newBlock.Index {
		return false
	}

	if oldBlock.Hash != newBlock.PrevHash {
		return false
	}

	if calculateHash(newBlock) != newBlock.Hash {
		return false
	}

	return true
}

What if we run into an issue where two nodes of our blockchain ecosystem both added blocks to their chains and we received them both. Which one do we pick as the source of truth? We choose the longest chain. This is a classic blockchain issue and has nothing to do with nefarious actors.

Two well meaning nodes may simply have different chain lengths, so naturally the longer one will be the most up to date and have the latest blocks. So let’s make sure the new chain we’re taking in is longer than the current chain we have. If it is, we can overwrite our chain with the new one that has the new block(s).

We simply compare the length of the slices of the chains to accomplish this:

func replaceChain(newBlocks []Block) {
	if len(newBlocks) > len(Blockchain) {
		Blockchain = newBlocks
	}
}

If you’ve made it this far, pat yourself on the back! We’ve basically written up the guts of our blockchain with all the various functions we need.

Now we want a convenient way to view our blockchain and write to it, ideally in a web browser so we can show our friends!

Web Server

We assume you’re already familiar with how web servers work and have a bit of experience wiring them up in Go. We’ll walk you through the process now.

We’ll be using the Gorilla/mux package that you downloaded earlier to do the heavy lifting for us.

Let’s create our server in a run function that we’ll call later.

func run() error {
	mux := makeMuxRouter()
	httpAddr := os.Getenv("ADDR")
	log.Println("Listening on ", os.Getenv("ADDR"))
	s := &http.Server{
		Addr:           ":" + httpAddr,
		Handler:        mux,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	if err := s.ListenAndServe(); err != nil {
		return err
	}

	return nil
}

Note that port we choose comes from our .env file that we created earlier. We give ourselves a quick console message with log.Println to let us know the server is up and running. We configure the server a bit and then ListenAndServe. Pretty standard Go stuff.

Now we need to write the makeMuxRouter function that will define all our handlers. To view and write to our blockchain in a browser, we only need 2 routes and we’ll keep them simple. If we send a GET request to localhost we’ll view our blockchain. If we send a POST request to it, we can write to it.

func makeMuxRouter() http.Handler {
	muxRouter := mux.NewRouter()
	muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
	muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
	return muxRouter
}

Here’s our GET handler:

func handleGetBlockchain(w http.ResponseWriter, r *http.Request) {
	bytes, err := json.MarshalIndent(Blockchain, "", "  ")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, string(bytes))
}

We simply write back the full blockchain, in JSON format, that we can view in any browser by visiting localhost:8080. We have our PORT variable set as 8080 in our `.env` file so if you change it, make sure to visit your correct port.

Our POST request is a little bit more complicated, but not by much. To start, we’ll need a new Message struct. We’ll explain in a second why we need it.

type Message struct {
	UniqueId int
}

Here’s the code for the handler that writes new blocks. We’ll walk you through it after you’ve read it.

func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
	var m Message

	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&m); err != nil {
		respondWithJSON(w, r, http.StatusBadRequest, r.Body)
		return
	}
	defer r.Body.Close()

	newBlock, err := generateBlock(Blockchain[len(Blockchain)-1], m.UniqueId )
	if err != nil {
		respondWithJSON(w, r, http.StatusInternalServerError, m)
		return
	}
	if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
		newBlockchain := append(Blockchain, newBlock)
		replaceChain(newBlockchain)
		spew.Dump(Blockchain)
	}

	respondWithJSON(w, r, http.StatusCreated, newBlock)

}

The reason we used a separate Message struct is to take in the request body of the JSON POST request we’ll use to write new blocks. This allows us to simply send a POST request with the following body and our handler will fill in the rest of the block for us:

{"UniqueId":50}

The 50 is an example UniqueId. Use your own by changing that integer to your own UniqueId.

After we’re done decoding the request body into our var m Message struct, we create a new block by passing in the previous block and our new pulse rate into the generateBlock function we wrote earlier . This is everything the function needs to create a new block. We do a quick check to make sure the new block is kosher using the isBlockValid function we created earlier.

A couple notes
  • spew.Dump is a convenient function that pretty prints our structs into the console. It’s useful for debugging.
  • for testing POST requests, we like to use Postmancurl works well too, if you just can’t get away from the terminal.

When our POST requests are successful or unsuccessful, we want to be alerted accordingly. We use a little wrapper function respondWithJSON to let us know what happened. Remember, in Go, never ignore errors. 

func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
	response, err := json.MarshalIndent(payload, "", "  ")
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("HTTP 500: Internal Server Error"))
		return
	}
	w.WriteHeader(code)
	w.Write(response)
}
Almost done!

Let’s wire all these different blockchain functions, web handlers and the web server in a short, clean main function:

func main() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		t := time.Now()
		genesisBlock := Block{0, t.String(), 0, "", ""}
		spew.Dump(genesisBlock)
		Blockchain = append(Blockchain, genesisBlock)
	}()
	log.Fatal(run())

}

So what’s going on here?

  • godotenv.Load() allows us to read in variables like our port number from the.env file we placed at the root of our directory so we don’t have to hardcode them (gross!) throughout our app.
  • genesisBlock is the most important part of the main function. We need to supply our blockchain with an initial block, or else a new block will not be able to compare its previous hash to anything, since a previous hash doesn’t exist.
  • We isolate the genesis block into its own go routine so we can have a separation of concerns from our blockchain logic and our web server logic. This will work without the go routine but it’s just cleaner this way.

Tada! We’re done!

How to build a cryptomining rig: Bitcoin mining

Cryptocurrency has been — and continues to be — a wild ride. 

I’m old enough to remember being given a couple of bitcoins when they were worth next to nothing. Needless to say, I don’t have them anymore. Now, with bitcoin and other cryptocurrency prices skyrocketing again, there’s renewed interest in cryptomining, which is a way to accumulate cryptocurrency without having to pay for it.

Let’s take a look at what makes a good cryptomining rig, and what hardware it takes if you want to be serious about mining.

What is cryptomining?

In the most basic terms, you are using a computer (or computers) to solve cryptographic equations and record that data to a blockchain. Taking this a bit deeper, miners verify the hashes of unconfirmed blocks and receive a reward for every hash that is verified. The process is computationally intensive, requiring state-of-the-art hardware if you are planning on making much headway with mining. Mining, as it was back in the days of the gold rush, is not for the faint of heart. 

And as with all high-end systems, it’s less a case of how much do you want to spend, and more a case of how fast do you want to spend. So, what hardware do you need to mine cryptocurrency?

What is a cryptomining rig?

OK, the “rig” is essentially a customized PC. It has all the common elements of a PC: CPU, motherboard, RAM, and storage. Where things deviate from the norm is when it comes to the graphics cards. It’s the GPU that’s doing that hard work when it comes to mining cryptocurrency, and not the CPU. You’re going to need quite a powerful GPU for mining, and likely you are going to be buying more than one. A lot more.

In fact, you can think of a mining rig as a relatively cheap PC with one or more high-performance GPUs attached. You need to connect multiple graphics cards to a single system, which means you also need a motherboard to handle that. You’ll also be looking at more than one power supply unit (PSU) if you’re planning to push things to the extremes.

There are also some other mining-specific items you’ll need to make the mining rig ready for mining.

MINING RIG CONSIDERATIONS

Here are a few considerations to bear in mind when building a mining rig:

  • It’s not going to be cheap! 
  • You need to factor power consumption in your mining equation because that can eat into your earnings.
  • You’re not building a regular PC, and getting everything to work can become a game of trial and error and a lot of fiddling with drivers. Be patient!

WHY ARE GRAPHICS CARDS PRICES SO ASTRONOMICAL?

Prices are being driven high by two factors:

  • Supply chain issues causing backlogs
  • In order to prevent high demand from miners causing even more issues, most cards now feature LHR (Lite Hash Rate) to limit mining speeds, making them less desirable for cryptomining. The card listed below is not limited, so the price making it perfect for mining.

Motherboard

Asus B250 Mining Expert

OK, let’s start with the motherboard. The Asus B250 Mining Expert is a beast of a motherboard, capable of having 19 graphics cards connected to it. That’s a lot. The board isn’t new — it was released in 2017 — and it is finicky when it comes to setting up (it needs a specific layout of AMD and Nvidia graphics cards),

Asus has published recommend GPU layouts for 19-, 13-, and 11-card for this board, and while other layouts might work, I recommend staying with what the manufacturer suggests, as veering away from this is a recipe for a serious — not to mention expensive — headaches.  

Note: Asus recommends running Windows 10 with this motherboard. $999 at Newegg

CPU

Intel Core i5-6500

There’s no real point in overspending on a CPU for a mining rig since it’s the GPU’s that are doing the hard work. This quad-core Core i5 is perfect for this setup and works great with the motherboard chosen above.$140 at Newegg

RAM

G.SKILL Aegis 16GB (2 x 8GB)

You’re not going to overspend on RAM either. The motherboard supports DDR4 2400, and this 2x8GB kit from G.SKILL fits the bill. $55 at Newegg

Storage

SanDisk SSD Plus 1TB

I’d install a couple of these 1TB SSDs. At under $100 each, they’re perfect for this kind of application. You could go with HDDs, but I prefer going with SSDs nowadays.

Also: Best external hard drives$85 at Newegg

PSU

Segotep 850W Full-Modular PSU

Depending on how many graphics cards you have installed, you may need multiple PSUs. It’s tempting to find the cheapest possible, but since they are going to be pushed hard, I recommend paying a little more.

These Segotep PSUs are middle-of-the-road good value, yet they offer reliable performance. The modular nature also means that you’re not turning the mining rig into a spaghetti of wires.$104 at Newegg

PCI-e Riser

FebSmart 16x to 1x Powered Riser 6-pack

Even if you’ve built a PC in the past, I bet you’ve not had to fit in PCI-E risers. This is where a bitcoin mining rig differs from a regular PC in that you can’t have all the graphics cards directly attached to the motherboard, so these risers allow you to connect them indirectly.

You’re going to need one of these for every card you connect (other than the card that goes into the x16 PCI-e slot). This six-pack of powered risers are great and provide stable power to your graphics cards.

I do not recommend using non-powered risers. I’ve had nothing but problems with stability using them in the past in cryptomining rigs, so don’t make the same mistake I made!$65 at Newegg

Nvidia graphics card

MSI Ventus 3X GeForce RTX 3090

This is a great card and everything you’re looking for in a mining rig. Loads of potential for overclocking, stable, and great cooling. Another nice side benefit is that it’s quite an efficient card, which means lower power consumption and reduced mining costs.

And, unlike a lot of graphics cards nowadays, this does not feature LHR (Lite Hash Rate) to limit mining speeds.

  • 24GB 384-Bit GDDR6X
  • Boost Clock 1725 MHz
  • 1 x HDMI 2.1 3 x DisplayPort 1.4a
  • 10496 CUDA Cores
  • PCI Express 4.0