Write your own blockchain with just 200 lines of Go code!

Source: Internet
Author: User
Tags blockchain go development environment go language hash calculation

“You can implement your own blockchain with less than 200 lines of Go code!” Sounds interesting? What better way to learn than to develop a blockchain of your own? Then let's practice together!


Because we are a technology company in the health care field, we use the heartbeat data (BPM heart rate) of human calm as the sample data in this article. Let's first count the number of heartbeats in your minute, then write down, this number may be used in the following content.


Through this article, you will be able to:


Create your own blockchain


Understand how the hash function maintains the integrity of the blockchain


How to create and add new blocks


How multiple nodes compete to generate blocks


View the entire chain through the browser


All other basics about blockchain


However, it will not be covered in consensus algorithm articles such as Workload Proof Algorithm (PoW) and Proof of Equity Algorithm (PoS). At the same time, in order to make you more clear about the blockchain and the addition of blocks, we simplify the process of network interaction. The process of P2P networks such as "full-network broadcast" will be added in the next article.


let's start!

Setting

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


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

Spew can help us directly view the struct and slice data structures in the console.


Go get github.com/gorilla/mux

Gorilla's mux package is very popular, we use it to write web handlers.


Go get github.com/joho/godotenv

Godotenv helps us read the .env configuration file in the project root so that we don't have to hardcode the configuration like http port into the code. For example like this:


ADDR=8080

Next, we create a main.go file. After that, most of our work is around this file, let me start coding!


Import dependency

We import all the dependencies in a declarative way:


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

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


Type block struct {

    Index int

    Timestamp string

    BPM int

    Hash string

    PrevHash string

}

Index is the position of this block in the entire chain


Timestamp is obviously the timestamp when the block was generated.


Hash is the hash value generated by this block through the SHA256 algorithm.


PrevHash represents the SHA256 hash value of the previous block


BPM heart rate per minute, which is the heart rate. Remember the article at the beginning of the article?


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


Var Blockchain []Block

We use the hash algorithm (SHA256) to determine and maintain the correct order of the blocks and blocks in the chain, ensuring that the PrevHash value of each block is equal to the Hash value in the previous block, so that the chain is constructed in the correct block order:


Hashing and generating blocks

Why do we need to hash? There are two main reasons:


Uniquely identify the data on the premise of saving space. The hash is calculated from the data of the entire block. In our example, the data of the entire block is calculated into a fixed-length unforgeable string by SHA256.


Maintain the integrity of the chain. 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 destroy the chain. Take the medical and health field we are engaged in. For example, if a malicious third party modifies the unhealthy BPM value in one or several blocks in order to adjust the price of “life insurance”, the whole chain becomes untrustworthy. It is.


We then write a function that computes the SHA256 hash value for 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)

}

The calculateHash function accepts a block that computes the SHA256 hash value from the Index, Timestamp, BPM, and PrevHash values in the block. Next we can carry a function that generates blocks:


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

}

Where Index is incremented from the index of the given previous block, the timestamp is obtained directly by the time.Now() function, the Hash value is calculated by the previous calculateHash function, and PrevHash is given by the previous one. The hash value of the block.


Check block

After getting the block generation, we need to have a function to help us determine if a block has been tampered with. Check the Index to see if the block is incremented correctly. Check if the PrevHash matches the Hash of the previous block, and then check the Hash value of the current block by calculateHash. Through these steps we can write a check function:


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

}

In addition to the check block, we also have a problem: both nodes generate blocks and add them to their respective chains. Which one should we prevail? The details here are left to the next article. Let us first remember one principle: always choose the longest chain.


In general, a longer chain means that its data (state) is updated, so we need a function


Can help us switch the local expired chain to the latest chain:


Func replaceChain(newBlocks []Block) {

    If len(newBlocks) > len(Blockchain) {

        Blockchain = newBlocks

    }

}

At this point, we basically complete all the important functions. Next, we need a convenient and intuitive way to view our chain, including data and status. Viewing web pages through a browser may be the most appropriate way!


Web service

I guess you must be very familiar with traditional web services and development, so you will definitely see this part.

With the Gorilla/mux package, let's write 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,

    }


    If err := s.ListenAndServe(); err != nil {

        Return err

    }


    Return nil

}

The port number is obtained by the aforementioned .env, and then add some basic configuration parameters, the web service can already listen and serve!

Next we will define different endpoints and corresponding handlers. For example, for a GET request for "/" we can look at the entire chain, and a "/" POST request can create a block.


Func makeMuxRouter() http.Handler {

    muxRouter := mux.NewRouter()

    muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")

    muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")

    Return muxRouter

}

Handler request 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))

}

For simplicity, we return the entire chain directly in JSON format. You can access localhost:8080 or 127.0.0.1:8080 in your browser (here 8080 is the port number ADDR you defined in .env).

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


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)

}

I’m almost done.

Next, we "assemble" these functions on the blockchain, the functions of the web service:


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())


}

The genesisBlock here is the most important part of the main function, which is used to initialize the blockchain. After all, the PrevHash of the first block is empty.


Oh yeah! finished

You can get the complete code from here: Github repo[1]

Let's start it:


Go run main.go

In the terminal, we can see the log information started by the web server and print out the information of the creation block:


Then we open the browser and access the address localhost:8080. We can see that the current information about the entire blockchain is displayed on the page (of course, there is only one Genesis block at the moment):



Next, we will send some POST requests via POSTMAN:



Refreshing the previous page, there are more blocks in the current chain, which we just generated, and you can see that the order and hash values of the blocks are correct.


Next step

We have just completed a blockchain of our own, although it is very simple, but it has basic capabilities such as block generation, hash calculation, block check. Next, you can continue to learn other important knowledge of the blockchain, such as workload algorithms, consensus algorithms such as proof of equity, or smart contracts, Dapp, sidechains, and more.


Currently this implementation does not include any P2P network content, we will add this part in the next article, of course, we encourage you to practice it on this basis!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.