• Code your own blockchain in less than 200 lines of Go!
  • Originally posted by Coral Health
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: Starrier
  • Proofread by ALVINYEH, github.com/SergeyChang

If this is not your first time reading this article, please read part 2 —here!

This tutorial is adapted from this excellent article on writing basic blockchains using JavaScript. We’ve ported it to Go and added some extra benefits — like viewing your blockchain in a Web browser. If you have any questions about the following tutorial, please be sure to join us on Telegram. Can consult any question to us!

The data samples in this tutorial will be based on your resting heartbeat. We are a healthcare company after all 🙂 For fun, record your pulse count in one minute (beats per minute) and memorize this number.

Almost every developer in the world has heard of blockchain, but most still don’t know how it works. They may only know about bitcoin because of it, or because they’ve heard of things like smart contracts. This article attempts to demystify blockchain by helping you write your own simple blockchain in Go, using less than 200 lines of code! By the end of this tutorial, you will be able to write and run your own blockchain locally, as well as view it in a Web browser.

What better way to learn about blockchain than by creating your own blockchain?

What will you be able to do

  • Create your own blockchain
  • How does Hash maintain the integrity of the blockchain
  • Know how to add new blocks
  • How does TieBreakers work when blocks are generated from multiple nodes
  • View the blockchain in a Web browser
  • Write the new block
  • Get the basics of blockchain so you can decide where your journey takes you from here!

What you can’t do

To keep this article simple, we will not deal with more advanced consensus concepts, such as proof of work and proof of interest. To allow you to view your blockchain and add blocks, we will simulate network interaction, but the webcast as the depth of the article will be retained.

Let’s get started!

The preparatory work

Now that we’ve decided to code in Go, we’ll assume that you already have some experience with Go. After installing and configuring Go, we’ll also need to get the following packages:

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

Spew allows us to view well-defined structs and slices in the console, and you deserve it.

go get github.com/gorilla/mux

Gorilla/ MUx is a common package for writing Web programs to handle. We’re going to use it.

go get github.com/joho/godotenv

Gotdotenv allows us to read.env files from the root directory so we don’t have to hardcode things like HTTP ports. We need that, too.

We create an.env file in the root directory and define the port to service the HTTP request. Just add a line to the file:

ADDR=8080

Create the main.go file. From now on, everything will be written into this file and will be coded in less than 200 code.

The import

Here’s what we need to import and the package declaration, and we’ll say 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"
)
Copy the code

The data model

We will define the struct for each block that makes up the blockchain. Don’t worry, we’ll wrap up the meaning of all these fields in a minute.

type Block struct {
	Index     int
	Timestamp string
	BPM       int
	Hash      string
	PrevHash  string
}
Copy the code

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

  • IndexIs where the data is recorded in the blockchain
  • TimestampIs to automatically determine the time at which data is written
  • BPMBeats per minute is your pulse rate
  • HashIs the SHA256 identifier that represents this data record
  • PrevHashIs the SHA256 identifier of the previous record in the chain

We also model the blockchain itself, which is just a slice in a Block:

var Blockchain []Block
Copy the code

So how does hashing fit into blocks and blockchains? We use hash tables to identify and maintain the correct order of blocks. By ensuring that the PrevHash in each Block is the same as the Hash in the previous Block Block, we know the correct order of the blocks that make up the chain.

Hash and generate new blocks

So why do we need hashes? We hashed the data for two main reasons:

  • To save space. A hash is derived from all data on a block. In our example, we have only a few data points, but suppose we have data from hundreds, thousands, or millions of previous blocks. Hashing data into a single SHA256 string or hashing these hash tables is much more efficient than copying all the data from the previous block over and over again.
  • Protect the integrity of blockchain. By storing the preceding hash, as we did in the figure above, we are able to ensure that the blocks in the blockchain are arranged in the correct order. If a malicious party tried to manipulate the data (for example, changing our heart rate to determine the price of life insurance), the hashing would quickly change, the chain would “break” and everyone would know and no longer trust the malicious chain.

Let’s write a function that takes Block data and creates the SHA256 hash value of I.


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

This calculateHash function takes the Block’s Index, Timestamp, BPM, we provide the Block’s PrevHash link as an argument and returns the SHA256 hash as a string. Now we can use a new generateBlock function to generate a new block that contains all the elements we need. We need to provide the block in front of it so that we can get its hash and pulse rate in BPM. Don’t worry about passing in BPM int arguments. We’ll talk about that later.

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

	var newBlock Block

	t := time.Now()

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

	return newBlock, nil
}
Copy the code

Notice that the current time is automatically written to the block using time.now (). Also notice that our previous calculateHash function was called. Copy from the hash of the previous block to PrevHash. Index increments from the Index of the previous block.

Block check

We need to write some functions to ensure that these blocks are not tampered with. We also do this by checking the indexes to ensure that they increase as expected. We will also check to make sure our PrevHash is the same as the Hash of the previous block. Finally, we want to recheck the hash of the current block by running the calculateHash function again on the current block. Let’s write an isBlockValid function that does all of this and returns a bool. If it passes all of our checks, it returns true:

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

	ifoldBlock.Hash ! = newBlock.PrevHash {return false
	}

	ifcalculateHash(newBlock) ! = newBlock.Hash {return false
	}

	return true
}
Copy the code

If we have a problem where both nodes in the blockchain ecosystem have added blocks to their chain, and we both receive them. Which do we choose as the source of truth? We choose the longest chain. This is a classic blockchain problem that has nothing to do with evil actors.

Two meaningful nodes may simply have different chain lengths, so it is natural that the longer node will be the latest and have the latest block. So let’s make sure that the new chain we’re accepting is longer than the one we already have. If so, we can overlay our chain with a new chain with a new block.

To achieve this, we simply compare the lengths of the chain pieces:

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

If you’ve been doing this consistently, give yourself a pat on the back! Basically, we have written the internal structure of the blockchain with the various functions we need.

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

The Web server

We assume that you are already familiar with how Web servers work and have some experience connecting them to Go. We’re going to walk you through the process right now.

We’ll use the Gorilla/ MUx package you downloaded earlier to do the heavy lifting for us.

We create the server in the run function we call later.

func run(a) 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
}
Copy the code

Note that the port we selected is from the.env file we created earlier. We use log.println to provide ourselves with a live console message to get our server up and running. We did some configuration on the weapon and then on ListenAndServe. Very standard Go.

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

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

This is our GET handler.

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))
}
Copy the code

We simply write back the complete blockchain in JSON format, which can be viewed in any browser by accessing localhost:8080. We set the ADDR variable to 8080 in the.env file. If you change it, make sure you access your correct port.

Our POST request is a bit complicated (not much). First, we need a new Message struct. We’ll explain why we need it later.

type Message struct {
	BPM int
}
Copy the code

Here is the code to write the handler for the new block. We’ll show you again when you’re done.

func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
	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()

	newBlock, err := generateBlock(Blockchain[len(Blockchain)- 1], m.BPM)
	iferr ! =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)

}
Copy the code

The reason for using the separate Message structure is the request body that receives the JSON POST request, which we will use to write the new block. 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:

{"BPM":50}

50 is an example of a pulse rate measured in minutes. Use an integer value to change your pulse rate.

After decoding the request body into the var M Message structure, a new block is created by passing in the previous block and passing the new pulse rate to the generateBlock function you wrote earlier. That’s all the function needs to create a new block. We use the isBlockValid function we created earlier to quickly check to make sure the new block is healthy.

Some notes

  • _spew.Dump_ Is a handy function that prints our structure to the console. This is useful for debugging.
  • For testing POST requests, we like to use Postman. Curl works fine, too, if you can’t leave the terminal.

We want to be notified when our POST request succeeds or fails. We use a little wrapper function respondWithJSON to let us know what’s going on. Remember, never ignore them in Go. Handle them gracefully.

func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
	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

It’s almost done!

Let’s link all the different blockchain functions, Web handlers, and Web servers into a short, clean main function:

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

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

}
Copy the code

What’s going on here?

  • godotenv.Load()Allows us to go from the root directory.envVariables like port numbers are read from the file so we don’t have to hardcode them throughout the application (ew!). .
  • genesisBlock 是 mainThe most important part of the function. We need to provide an initial block for our blockchain, otherwise the new block won’t be able to compare its previous hash to anything, because the previous hash doesn’t exist.
  • We isolated the initial block into its own Go routine so that we could separate our concerns from our blockchain logic and our Web server logic. But it just works in a more elegant way without the Go routine.

That’s great! We’re done!

Here is the full code:

  • mycoralhealth/blockchain-tutorial: Blockchain – Tutorial – Write and publish your own blockchain in less than 200 lines of Gogithub.com

Now let’s talk about something interesting. Let’s try it 🙂

Use go Run main.go to start the application from the terminal

In the terminal, we see that the Web server is up and running, and we have a printout of our initial block.

Now use your port number to access localhost, which for us is 8080. As expected, we see the same initial block.

Now, let’s send some POST requests to add blocks. With Postman, we will add some new blocks with different BPM.

Let’s refresh the browser. Voila, we now see all the new blocks in the chain with the PrevHash of the new block matching the Hash of the old block, just as we expected!

The next step

A: congratulations! You just write your own chain of blocks with proper hashing and block validation. You should now be able to take control of your own blockchain journey and explore more complex topics such as proof of work, proof of interest, smart contracts, DApps, side chains, and more.

What this tutorial does not discuss is how to use proof of work to mine new blocks. This will be a pure tutorial, but plenty of blockchain exists and no proof of the working mechanism. In addition, broadcasting is currently simulated by writing and viewing blockchains in a Web server. There is no P2P component in this tutorial.

If you want us to add such content as job certification and interpersonal relationship, please be sure to tell us in our Telegram, and follow us on Twitter! This is the best way to communicate with us. Ask us questions, give us feedback, and suggest new tutorials. We’d like to hear your opinion.

By popular demand, we’ve added a follow-up to this tutorial! Look at them!

  • Blockchain networks.
  • Encode your own blockchain mining algorithm!
  • Learn how to use IPFS to store data via blockchain.
  • Write your own stump algorithm proof!

To learn more about coral health and how we’re using blockchain to advance personalized medicine/therapy 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.