- Code your own blockchain mining algorithm in Go!
- Originally posted by Coral Health
- The Nuggets translation Project
- Permanent link to this article: github.com/xitu/gold-m…
- Translator: EmilyQiRabbit
- Proofreader: Stormluke, Mingxing47
If you have any questions or suggestions for the tutorial below, join our Telegram message group and ask us anything you want!
With the recent bitcoin and Ethereum mining fires, it’s easy to wonder what all the fuss is about. For newcomers to the space, they hear crazy stories of people filling warehouses with Gpus and earning millions of dollars worth of cryptocurrency every month. What exactly is e-money mining? How does it work? How can I try to write my own mining algorithm?
In this blog post, we’ll take you through each of these questions and finally complete a tutorial on how to write your own mining algorithm. The algorithm we’re going to show you is called proof of work, and it’s the basis of bitcoin and Ethereum, two of the most popular digital currencies. Don’t worry, we’ll explain how it works in a minute.
What is e-money mining
To be valuable, electronic money needs to be scarce. If anyone could produce as many Bitcoins as they wanted, whenever they wanted, bitcoin would be worthless as a currency. (Wait, didn’t the Federal Reserve do that? The bitcoin algorithm will hand out bitcoins to a winning member of the network every 10 minutes, giving it a maximum supply of about 122 years. Since the rationed supply is not fully issued at the outset, this issuance schedule also keeps inflation in check to some extent. As time goes by, the release rate will get slower and slower.
The process of deciding who the winner is and giving away the bitcoin requires him to do a certain amount of “work” and compete with others who are also doing the job. This process is called mining because it is much like a miner who takes time to finish the job and eventually (hopefully) find gold.
The bitcoin algorithm requires participants, or nodes, to do work and compete with each other to ensure that bitcoin doesn’t launch too quickly.
How does mining work?
A quick Google search for “How does bitcoin mining work?” Will give you pages of answers explaining that bitcoin mining requires nodes (you or your computer) to solve a difficult mathematical problem. While this is technically true, simply calling it a “mathematical” problem is understated and trite. It is very interesting to understand the inner workings of mining. To learn how mining works, we first need to understand encryption algorithms and hashing.
A brief introduction to hash encryption
One-way encryption takes the input value as readable plaintext, such as “Hello world”, and imposes a function on it (that is, a mathematical problem) to produce an unreadable output. These functions (or algorithms) vary in nature and complexity. The more complex the algorithm, the harder it is to decrypt the inverse. As a result, encryption algorithms can provide strong protection for things like user passwords and military codes.
Let’s look at an example of SHA-256, a popular encryption algorithm. This hash site allows you to easily calculate sha-256 hashes. Let’s hash “Hello world” to see what we get:
Try repeating the hash for “Hello World”. You’re going to get the same hash every time. Given a program with the same input, repeated computations will produce the same result, which is called idempotence.
One of the basic properties of encryption algorithms is that it is difficult to calculate the input value by reverse calculation, but it is easy to verify the output value. For example, using the sha-256 hash algorithm described above, it would be easy for someone else to apply the SHA-256 hash algorithm to “Hello World” to verify that it does output the same hash value, but it would be very difficult to deduce “Hello World” from the hash value. That’s why this kind of algorithm is called one-way.
Bitcoin uses a dual SHA-256 algorithm, which simply applies SHA-256 to the “Hello World” SHA-256 hash again. In this tutorial, we will only apply the SHA-256 algorithm.
dig
Now that we know what the encryption algorithm is, we can get back to cryptocurrency mining. Bitcoin needs to find some way to make people who want bitcoin “work” so it doesn’t launch too quickly. Bitcoin works by having participants hash numbers and letters over and over again until they find the result that begins with a “0” with a specific digit number.
For example, go back to the hash site and hash “886”. It will generate a hash with a prefix of three zeros.
But how do we know that “886” can produce a result that starts with three zeros? That’s the point. Before writing this blog, we didn’t know. In theory, we need to walk through all the combinations of numbers and letters, test the results, until we get a result that matches the three zeros we need. To give you a simple example, we actually did a pre-calculation and found that the hash value of “886” starts with three zeros.
The fact that anyone can easily verify that the hash result of “886” is three zeros proves that I did a lot of work testing and checking many combinations of letters and numbers to get this result. So, if I’m the first to get this result, I’ll be able to get bitcoin by proving THAT I did the work — proof that anyone can easily verify that the hash of “886” is a three-zero prefix, as I claim. That’s why the Bitcoin consensus algorithm is called proof of work.
But what if I get lucky and I get the three-zero prefix on my first try? This is almost impossible, and the nodes that occasionally succeed in digging a block the first time (proving they did the work) are overwhelmed by thousands of other blocks that did the extra work to find the right hash. Try typing any other combination of letters and numbers on the hash site. I bet you don’t get a result that starts with three zeros.
Bitcoin’s requirements are much more complex than that (more zeros!). And can adjust requirements dynamically to ensure that the job is neither too hard nor too easy. Remember, the goal is to issue bitcoin every 10 minutes, so if too many people are mining, you’ll need to make the proof of work harder to complete. So we have difficulty adjusting the difficulty. For our purposes, adjusting the difficulty means requiring more zero prefixes.
Now you see, bitcoin consensus is a lot more fun than just “solving a math problem”!
Enough background for that. Let’s start programming!
Now that we have enough background, let’s build our own Bitcoin program using workload consensus algorithms. We’re going to write it in Go because we use it at Coral Health and, frankly, it’s awesome.
Before moving on to the next step, I encourage readers to read our previous post, Code Your Own blockchain in Less than 200 lines of Go! . It’s not a hard and fast requirement, but the examples below will be rough. If you need more details, check out the previous blog. If you’re familiar with the previous article, skip ahead to the “Proof of Work” section below.
structure
We’ll have a Go service, and we’ll simply put all the code in a main. Go file. This file will provide all the blockchain logic we need (including the proof-of-work algorithm) and include all the REST interface handlers. Blockchain data is immutable, we only need GET and POST requests. We’ll use the browser to send GET requests to see the data, and use Postman to send POST requests to new blocks (curl is also useful).
Led package
We start with the standard import operation. Be sure to use Go Get to get the following package
github.com/davecgh/go-spew/spew beautifully prints out your blockchain at the terminal
github.com/gorilla/mux a handy layer to connect to your Web services
github.com/joho/godotenv reads your environment variables in the.env file in the root directory
Let’s create an.env file in the root directory that contains only one environment variable that we’ll use later. Write a line in the.env file: ADDR=8080.
Declare the package and introduce it in the main.go definition of the root directory:
package main
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
)
Copy the code
If you read the previous article, you should remember this diagram. Blocks in a blockchain can be validated by comparing the previous hash attribute value of the block to the hash value of the previous block. This is how blockchains protect their integrity and why hacker groups cannot modify the history of blockchains.
BPM is your heart rate, or the number of beats per minute. We’re going to use the number of times your heart beats in a minute as data that we put into the blockchain. Put two fingers on your wrist and count the number of pulses per minute. Remember that number.
Some basic probes
Let’s add some data models and other variables to the main.go file that will be needed after introduction
const difficulty = 1
type Block struct {
Index int
Timestamp string
BPM int
Hash string
PrevHash string
Difficulty int
Nonce string
}
var Blockchain []Block
type Message struct {
BPM int
}
var mutex = &sync.Mutex{}
Copy the code
Difficulty is a constant that defines the number of zero prefixes we want to hash the result. The more zeros you have to get, the harder it is to find the correct hash input. Let’s start with a zero.
A Block is a data model for each Block. Don’t worry if you don’t understand Nonce, we’ll explain that later.
Blockchain is a series of blocks, representing a complete chain.
Message is the Message we send in with a POST request in the REST interface to generate a new Block.
We declare a mutex that will be used later to prevent data contention and ensure that no more than one block is created at the same time.
The Web service
Let’s connect to the Internet quickly. Create a run function and call it later in Main to support the service. You also need to declare route handlers in makeMuxRouter(). Remember, we only need the GET method to trace the contents of the blockchain and the POST method to create the block. Blockchain is not modifiable, so we do not need to modify and delete operations.
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,
}
iferr := s.ListenAndServe(); err ! = nil {return err
}
return nil
}
func makeMuxRouter() http.Handler {
muxRouter := mux.NewRouter()
muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
return muxRouter
}
Copy the code
HttpAddr := os.getenv (“ADDR”) will pull port :8080 from the.env file we just created. We can access the application by accessing the browser’s [http://localhost:8080](http://localhost:8080).
Let’s write a GET handler to print out the blockchain on the browser. We’ll also add a simple respondwithJSON function that will give us an error message in JSON format when an error occurs calling the interface.
func handleGetBlockchain(w http.ResponseWriter, r *http.Request) {
bytes, err := json.MarshalIndent(Blockchain, ""."")
iferr ! = nil { http.Error(w, err.Error(), http.StatusInternalServerError)return
}
io.WriteString(w, string(bytes))
}
func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
w.Header().Set("Content-Type"."application/json")
response, err := json.MarshalIndent(payload, ""."")
iferr ! = nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("HTTP 500: Internal Server Error"))
return
}
w.WriteHeader(code)
w.Write(response)
}
Copy the code
Remember, if you find this part too sketchy, please refer to itThe previous articleEach step of this section is explained in more detail here.
Now let’s write the POST handler. This function is how we add a new block. We use Postman to send a POST request, send a JSON body, such as {” BPM “:60}, to [http://localhost:8080](http://localhost:8080) with your previously measured heart rate.
func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type"."application/json")
var m Message
decoder := json.NewDecoder(r.Body)
iferr := decoder.Decode(&m); err ! = nil { respondWithJSON(w, r, http.StatusBadRequest, r.Body)return
}
defer r.Body.Close()
//ensure atomicity when creating new block
mutex.Lock()
newBlock := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
mutex.Unlock()
if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
Blockchain = append(Blockchain, newBlock)
spew.Dump(Blockchain)
}
respondWithJSON(w, r, http.StatusCreated, newBlock)
}
Copy the code
Note mutex’s lock and unlock. Before writing a new block, the blockchain needs to be locked, otherwise multiple writes will result in data contention. Savvy readers will also notice the generateBlock function. This is the key function for handling proof of work. We’ll talk about that later.
Basic blockchain functions
Before starting the proof-of-work algorithm, let’s connect the basic blockchain functions. We will add an isBlockValid function to ensure that the index is properly incremented and that the PrevHash of the current block matches the Hash value of the previous block.
We’ll also add a calculateHash function that generates the Hash values we need to create the Hash and PrevHash. It is an index, timestamp, BPM, previous block hash, and sha-256 hash of the Nonce (we’ll explain what that is later).
func isBlockValid(newBlock, oldBlock Block) bool {
ifoldBlock.Index+1 ! = newBlock.Index {return false
}
ifoldBlock.Hash ! = newBlock.PrevHash {return false
}
ifcalculateHash(newBlock) ! = newBlock.Hash {return false
}
return true
}
func calculateHash(block Block) string {
record := strconv.Itoa(block.Index) + block.Timestamp + strconv.Itoa(block.BPM) + block.PrevHash + block.Nonce
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hex.EncodeToString(hashed)
}
Copy the code
Proof of work
Let’s look at the mining algorithm, or proof of work. We want to ensure that the proof-of-work algorithm is completed before allowing a new Block to be added to the blockchain. We start with a simple function that checks whether the hash generated in the proof-of-work algorithm meets the requirements we set up.
Our requirements are as follows:
- The hash generated by the proof-of-work algorithm must start with a certain number of zeros
- The number of zeros consists of constants
difficulty
Determines that it is defined at the beginning of the program (in the example, it is 1) - We can make the proof-of-work algorithm difficult by increasing the difficulty value
Complete the following function, isHashValid:
func isHashValid(hash string, difficulty int) bool {
prefix := strings.Repeat("0", difficulty)
return strings.HasPrefix(hash, prefix)
}
Copy the code
Go provides convenient Repeat and HasPrefix functions in its Strings package. We define the variable prefix as a copy of the zero we defined in difficulty. Now let’s verify the hash value to see if it starts with these zeros, and return True if it does, False otherwise.
Now we create the generateBlock function.
func generateBlock(oldBlock Block, BPM int) Block {
var newBlock Block
t := time.Now()
newBlock.Index = oldBlock.Index + 1
newBlock.Timestamp = t.String()
newBlock.BPM = BPM
newBlock.PrevHash = oldBlock.Hash
newBlock.Difficulty = difficulty
for i := 0; ; i++ {
hex := fmt.Sprintf("%x", i)
newBlock.Nonce = hex
if! isHashValid(calculateHash(newBlock), newBlock.Difficulty) { fmt.Println(calculateHash(newBlock)," do more work!")
time.Sleep(time.Second)
continue
} else {
fmt.Println(calculateHash(newBlock), " work done!")
newBlock.Hash = calculateHash(newBlock)
break}}return newBlock
}
Copy the code
We create a newBlock and place the hash of the previous block in the PrevHash property to ensure continuity of the blockchain. The values of the other attributes are clear:
Index
The incrementalTimestamp
Is a string that represents the current timeBPM
It’s the heart rate you recorded earlierDifficulty
I get it directly from the constants at the beginning of the program. We will not use this property in this tutorial, but it is useful if we need to do further verification and confirm that the difficulty value is fixed to the hash result (that is, the hash result starts with N zeros so the difficulty value should also be equal to N, otherwise the blockchain is broken).
The for loop is the key part of this function. Let’s take a closer look at what’s done here:
- We’re going to set
Nonce
Is equal to thei
The hexadecimal representation of. We need a delta functioncalculateHash
A method of adding a changing value to the generated hash so that if we don’t get the number of zero prefixes we want, we can try again with a new value.The value of the change that we added to the concatenated string**calculateHash**
It’s called a Nonce. - In the loop, we use
i
And the Nonce starting with 0 evaluates the hash value and checks if the result is constantdifficulty
The number of zeros defined begins. If not, we start the next cycle with an incremental Nonce and try again. - We added a one-second delay to simulate the time it took to solve the proof-of-work algorithm
- We loop until we get the zero prefix we want, which means we have successfully completed the proof of work. If and only if we are allowed to
Block
throughhandleWriteBlock
The handler function is added toblockchain
.
Now that we have written all the functions, let’s finish main:
func main() {
err := godotenv.Load()
iferr ! = nil { log.Fatal(err) } gofunc() {
t := time.Now()
genesisBlock := Block{}
genesisBlock = Block{0, t.String(), 0, calculateHash(genesisBlock), "", difficulty, ""}
spew.Dump(genesisBlock)
mutex.Lock()
Blockchain = append(Blockchain, genesisBlock)
mutex.Unlock()
}()
log.Fatal(run())
}
Copy the code
We Load the environment variables using the godotenv.load () function, which is used for browser access: port 8080.
A Go routine creates the Genesis block because we need it as the starting point of the blockchain
We start the network service with the run() function we just created.
Done! It’s time to run it!
Here’s the complete code.
- mycoralhealth/blockchain-tutorial: blockchain-tutorial – Write and publish your own blockchain in less than 200 lines of Go_github.com
Let’s try to run this baby!
Start the program with go run main.go
Then use your browser to access [http://localhost:8080](http://localhost:8080) :
Genesis block has been created for us. Now open Postman and send a POST request to send the previously measured heart rate in JSON format to the same route in the body.
After sending the request, look at the terminal to see what happens. You’ll see your machine busy creating new hash values by increasing the Nonce value until it finds the zero prefix value it needs.
When the proof-of-work algorithm is complete, we get a very useful work done! Message, we can verify the hash value to see if it really starts with zero as we set it. This means that in theory, the new block where we are trying to add BPM = 60 information has already been added to our blockchain.
Let’s refresh the browser and see:
Success! Our second block has been added to the Genesis block. This means that we have successfully sent the block in the POST request, which triggers the mining process, and it will only be added to the blockchain if and only if the proof-of-work algorithm is complete.
The following
Great! What you just learned is really important. Proof-of-work algorithms are the foundation of Bitcoin, Ethereum, and many other large blockchain platforms. What we’ve just learned is not trivial; Although we used a low difficulty value in the example, increasing it to a large value is exactly how the blockchain proof-of-work algorithm works in a production environment.
Now that you have a clear understanding of the core parts of blockchain technology, it’s up to you to learn what to do next. I recommend the following resources to you:
- Learn how networked blockchains workin our Networking tutorial.
- Learn how to store large files distributed and communicate with blockchain in our IPFS tutorial.
If you’re ready to make another technological leap, try learning the Proof of Stake algorithm. While most blockchains use proof-of-work algorithms as consensus algorithms, proof-of-equity algorithms are gaining more and more attention. Many believe ethereum will switch from proof of work to proof of equity in the future.
Want to see a comparison tutorial on proof of work algorithm and proof of equity algorithm? Found an error in the code above? Like what we do? Hate what we do?
through Join our Telegram message group Let us know what you think! You will receive enthusiastic responses from the author of this tutorial and the rest of the Coral Health team.
To learn more about Coral Health and how we’re using blockchain to improve personal medicine research, visit our website.
The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.