Blockchain In Rust #02: Mining — Companion Guide

Hashing plays a vital and innovative role in the production and security of cryptocurrencies

encody
GeekLaunch
5 min readFeb 22, 2019

--

This is the second post in the companion guide to the video series Blockchain In Rust.

Rehashing

If you remember back from the last post, we prefer hash functions which, when the input data for it is changed even a little bit, produce unpredictable output (unless you actually perform the hashing algorithm). This property is called “avalanching.” Additionally, for the purposes of this project, we can treat hashes as large, arbitrary numbers or fixed-size, arbitrary byte arrays. These premises allow us to do some interesting things with hashes.

For example, I wrote a program that looked a little like this:

  1. Calculate the hash of the byte string "GeekLaunch". (It’s "a17d5669f2148e2982baab7c0b4c7d81100c7cf52c45a8d7deb429aeba156ea6")
  2. Calculate the hash of four arbitrary bytes.
  3. If the first three bytes of the step 1 hash and the step 2 hash match, print those three arbitrary bytes and their hash and exit. Otherwise, go back to step 2.

This program ran on my computer for about 3 minutes before it exited. Imagine how long it would take to find a complete, 100% match this way! (Hint: you’d probably have to sample more than four bytes.)

Here is the result:

1709934: [110, 23, 26, 0] -> "a17d560dfc6c675b65b33643fe8156f5ccdf97d8120e70c3c2e27cdc55142c50"

Both of these hashes start with a17d56: success!

Believe it or not, this little demo is strikingly similar to mining.

Mining in theory

Anyone using a cryptocurrency wants to know that their funds are secure. I think I can pretty safely say that’s a given for any currency, crypto- or not. The problem with cryptocurrencies, however, is that there is no real physical exchange that can unequivocally determine who is in possession of a quantity of currency. I can hand you a dollar; I can’t “hand you” a Bitcoin. Digital information is essentially infinitely replicable at zero cost: I can freely send you information while also keeping it for myself.

The distributed ledger use case of the blockchain helps to fix this problem in tandem with miners: computers that validate every transaction that passes through the network. Transactions go through the network in groups called blocks. Miners perform their services in exchange for a chance to mine a block of transactions and then collect the block reward.

It works like this:

A cryptocurrency needs to ensure that its users aren’t generating money from nowhere, stealing someone else’s money, etc, so everybody has a copy of the blockchain, and we make sure there are always eyes on every transaction.

Those “eyes” on every transaction are the miners: computers that are reading and verifying every single transaction. Of course, they must be rewarded for their work, but there must be checks on them to ensure they’re being honest too!

The check on the miners is built into the mining problem. It has intentionally been engineered such that it is difficult to solve, but easy to verify whether a solution is valid. Therefore, simply by providing the solution, a miner can prove to us they put in the effort toward solving the problem. Before solving this problem, however, the miners must validate every transaction in the block. This incentivizes miners to be honest, because if they aren’t, and they solve the problem, we can easily check their solution and throw it out.

If the miner is honest and solves (mines) the block, they are entitled to add the mined block to the blockchain and collect the block reward. Their solution checks out, and all of the transactions in the shiny new block are considered verified.*

(Side note: This does mean that Bitcoin’s (and many other cryptocurrencies’) blockchains are completely public, meaning that anyone in the world can view every transaction that has ever occurred via Bitcoin. This makes Bitcoin, at best, lightly salted with anonymity. Your financial history with Bitcoin is about as private as your Reddit username is secret—nobody knows it, right?)

Similarly to the matching variable defined in the code example in the first part of this post, mining has a parameter called difficulty. The specific implementation we’ll be discussing in this series is a bit different from Bitcoin’s version, but the concepts are the same. Basically, we’ll be reinterpreting the bytes of a block hash to be a number and comparing it to the difficulty value. If it’s lower than the difficulty value, we’ve mined the block. Otherwise, we try again.

But wait — if we just try again, we’ll get the exact same hash! Somehow, we need to change the bytes in the block. Obviously we can’t go around changing the contents of transactions and whatnot willy-nilly, so instead, we’ll use the field in our Block struct we called nonce. Like the example at the beginning, we can arbitrarily change the value of the nonce and re-hash the block.

Our goal is to make the hash lower than the difficulty value, but we can’t predict the value of the hash, so there’s no practical way to predict what nonce value we should choose. That means we simply have to try values until we find one that works (again, just like the example above).

It might help to think of mining a block like unlocking a lock. You have a pile of a few zillion keys, and you know that some of them work, but not all of them. Since you can’t see inside the lock to tell what the teeth on the correct keys look like, you just have to try one after another until one fits. Each different key that you try is like a different nonce attempt, and the difficulty value specifies how precisely a key must fit in order to unlock the lock.

Mining in code

We’ll attach a mine(&mut self) -> () function to our Block struct.

pub fn mine (&mut self) {
for nonce_attempt in 0..(u64::max_value()) {
self.nonce = nonce_attempt;
let hash = self.hash();
if check_difficulty(&hash, self.difficulty) {
self.hash = hash;
return; // all done!
}
}
}

This is very similar to the example script at the beginning of this post:

  1. Choose a nonce. In this case, we’ll try every value from 0 to 18,446,744,073,709,551,615.
  2. Attach it to the block.
  3. Hash the block.
  4. See if the block’s new hash fits its required difficulty. If it does, we’re done. If not, go back to step 1.

Conclusion

Now that we’ve got mining under our belts, let’s take a closer look at blockchain verification!

You can find the code for this installment in its entirety on GitHub.

Happy coding!

--

--