In this article, you will create your own blockchain in Go(Golang), understand how hash functions keep a blockchain intact, learn how to create and add new blocks in Go(Golang), allow multiple nodes to compete to generate blocks, view the entire chain through a browser, and learn all the other basics of blockchain.

Workload, however, the article will not involve prove algorithm (PoW) and interest (PoS) such consensus algorithm, at the same time in order to make you more clearly see the chain block and block to add, we will network interaction process simplified, about P2P networks such as the entire network broadcasting “content such as the process will be up in future articles.

Go(Golang) language development environment

We assume that you already have some experience developing the Go language. After installing and configuring the Go development environment, we also get the following dependencies:

~$ go get github.com/davecgh/go-spew/spew
Copy the code

Spew allows us to view struct and Slice data structures directly in the terminal.

~$ go get github.com/gorilla/mux
Copy the code

Gorilla’s MUx package is very popular and we use it to write Web handlers.

~$ go get github.com/joho/godotenv
Copy the code

Godotenv helps us read the. Env configuration file in the project root directory so that we don’t have to hard-code configuration like HTTP ports into our code. Like this:

ADDR=8080
Copy the code

Next, we create a main.go file. Most of the rest of the work revolves around this file, so start writing code!

Importing dependency packages

We import all dependency packages declaratively:

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

Blockchain data model

Next, we define a structure that represents the data model for each block that makes up the blockchain:

type Block struct {
    Index     int
    Timestamp string
    BPM       int
    Hash      string
    PrevHash  string
}
Copy the code
  • Index is the position of this block in the chain
  • Timestamp is obviously the Timestamp at which the block was generated
  • The Hash is the Hash value generated by the SHA256 algorithm for this block
  • PrevHash represents the SHA256 hash value of the previous block
  • BPM Number of heartbeats per minute, or heart rate

Next, we define a structure to represent the entire chain. The simplest representation is the slice of a Block:

var Blockchain []Block
Copy the code

We use the Hash algorithm (SHA256) to determine and maintain the correct order of blocks and blocks in the chain, ensuring that the PrevHash value of each block equals the Hash value of the previous block, thus building the chain in the correct order of blocks:

Blockchain hashes and generates new blocks

Why do we need hashes? There are two main reasons:

  • Uniquely identify data to save space. The hash is computed from the entire block of data, which in our case is computed by SHA256 into a fixed-length, unforgeable string.
  • Maintain chain integrity. By storing the hash value of the previous block, we can ensure that each block is in the correct order in the chain. Any tampering with the data will change the hash value and break the chain. For example, in our healthcare sector, if a malicious third party modifies unhealthy BPM values in one or more blocks in order to adjust the price of “life insurance”, the whole chain becomes distrusted.

We then write a function that computes the SHA256 hash value of the given data:

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

The calculateHash function takes a block and calculates the SHA256 hash from the Index, Timestamp, BPM, and PrevHash values in the block. We can then write a function that generates a block:

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

Where Index is derived from the Index increment of the given previous block, the timestamp is obtained directly from the time.now () function, the Hash value is calculated from the calculateHash function above, and the PrevHash is the Hash value of the given previous block.

Block chain check block

Now that the block is generated, we need a function to help us determine if a block has been tampered with. Check Index to see if the block is properly incremented, check PrevHash to see if it matches the Hash of the previous block, and check the Hash value of the current block with calculateHash. With these steps we can write a check function:

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

In addition to checkblocks, we have another problem: both nodes generate blocks and add them to their chains, so who should we use? I’ll save the details for the next article, but here’s a rule to keep in mind: Always choose the longest chain:

In general, a longer chain indicates that its data (state) is updated, so we need a function that will help us switch the local stale chain to the latest one:

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

At this point, we’ve pretty much done all the important functions. Next, we need a convenient and intuitive way to view our chain, including data and state. Viewing a Web page through a browser is probably the most appropriate way!

Go(Golang) language Web services

I’m guessing you’re familiar with traditional Web services and development, so this is a quick read.

With the Gorilla/ MUx package, we start by writing a function to initialize our Web service:

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

Env. Add some basic configuration parameters and the Web service is ready to listen and serve!

Now let’s define the different endpoints and their corresponding handlers. For example, a GET request for a “/” can see the chain, and a POST request for a “/” can create blocks.

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

GET request 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

For simplicity, we return the entire chain directly in JSON format, which you can view in your browser by visiting localhost:8080 or 127.0.0.1:8080 (8080 is the port ADDR you defined in.env).

The payload of a POST request is defined as the payload of a POST request.

type Message struct {
    BPM int
}
Copy the code

Let’s look at the handler implementation:

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

We can use the payload defined above in the body of the POST request, for example:

{"BPM": 75}Copy the code

Remember the generateBlock function we wrote earlier? It takes a “previous block” parameter, and a BPM value. The POST handler accepts the request and gets the BPM value in the request body, and then generates a new block with the help of the block-generating function and the block-checking function!

In addition, you can:

  • The spew.Dump function allows you to print struct, slice, and other data to the console in a very nice and easy to read way for debugging.
  • When testing a POST request, you can use the Chrome plugin POSTMAN, which is more intuitive and convenient than curl.

After the POST request is processed, whether the block was created successfully or not, we need to return a response to the client:

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

We’re almost done.

Next, we “assemble” the blockchain functions, the Web services functions:

func main() {
    err := godotenv.Load()
    iferr ! = nil { log.Fatal(err) } gofunc() {
        t := time.Now()
        genesisBlock := Block{0, t.String(), 0, "".""}
        spew.Dump(genesisBlock)
        Blockchain = append(Blockchain, genesisBlock)
    }()
    log.Fatal(run())
}
Copy the code

The genesisBlock here is the most important part of the main function that initializes the blockchain, after all the PrevHash of the first block is empty.

Oh yeah! To complete the

You can get the full code here: Github repo

Let’s start it up:

~$ go run main.go
Copy the code

In the terminal, we can see the log information of the Web server startup and print the genesis block information:

Then we open the browser and go to localhost:8080. We can see that the page displays the current information of the entire blockchain (of course, there is only one Genesis block at present) :

Next, we send some POST requests via POSTMAN:

Refresh the page, and now we have some blocks in the chain that we just generated, and you can see that the order of the blocks and the hash values are correct.

conclusion

We have just completed a blockchain of our own. It is very simple, but it has the basic capabilities of block generation, hash calculation, block verification and so on. You can then move on to other important knowledge of blockchain, such as consensus algorithms such as proof of work and proof of equity, or smart contracts, DApps, side chains, etc.

Currently this implementation does not include any P2P network content, we will cover this in the next article, of course, we encourage you to try it out on your own!

If you want to learn about ethereum DApp development effectively, you can visit the most popular online interactive tutorials provided by Wisdom:

  • Suitable for blockchain novices Ethereum DApp combat introductory tutorial
  • Blockchain +IPFS+Node.js+MongoDB+Express decentralized ethereum e-commerce application development practice

More content can also be found on this Ethereum blog.

Code your own blockchain in less than 200 lines of Go!